diff options
| author | Jabier Arraiza Cenoz <jabier.arraiza@marker.es> | 2015-07-24 19:53:33 +0000 |
|---|---|---|
| committer | Jabiertxof <jtx@jtx.marker.es> | 2015-07-24 19:53:33 +0000 |
| commit | 5a37f06c70e0e4e2519812564f4dcdced7c0246a (patch) | |
| tree | f959f73813cb6f968072aee1100323b24e294136 /src | |
| parent | update to trunk (diff) | |
| parent | Fix a bug continuing a bezier path whith a LPE one like spiro or bspline on a... (diff) | |
| download | inkscape-5a37f06c70e0e4e2519812564f4dcdced7c0246a.tar.gz inkscape-5a37f06c70e0e4e2519812564f4dcdced7c0246a.zip | |
update to trunk
(bzr r13879.1.17)
Diffstat (limited to 'src')
605 files changed, 30346 insertions, 19363 deletions
diff --git a/src/2geom/!PLEASE DON'T MAKE CHANGES IN THESE FILES.README b/src/2geom/!PLEASE DON'T MAKE CHANGES IN THESE FILES.README index 3ced704c7..e94a549f6 100644 --- a/src/2geom/!PLEASE DON'T MAKE CHANGES IN THESE FILES.README +++ b/src/2geom/!PLEASE DON'T MAKE CHANGES IN THESE FILES.README @@ -1,9 +1,13 @@ -All code files in this directory are *direct* copies of the files in 2geom's BZR repo. -If you want to change the code, please change it in 2geom, then copy the files here. -Otherwise, I will probably miss that you changed something in Inkscape's copy, and -destroy your changes by copying 2geom's files over it during the next time I update -Inkscape's copy of 2geom. - - Johan Engelen +This is an in-tree copy of lib2geom, a 2D geometry library started +by Inkscape developers. If you want to change code in 2Geom, you should +commit it first to upstream repository and then execute this command: + +rsync -r --existing --exclude CMakeLists.txt /path/to/lib2geom/src/2geom/ /path/to/inkscape/src/2geom/ + +The command above will only update existing files. If you add new files +to 2Geom, you'll need to copy the new files manually. Same if you remove +some files. Note that the trailing slashes are required! 2geom's BZR repo = lp:lib2geom -http://lib2geom.sourceforge.net
\ No newline at end of file +http://lib2geom.sourceforge.net + diff --git a/src/2geom/2geom.h b/src/2geom/2geom.h index 000f3423d..813e243b3 100644 --- a/src/2geom/2geom.h +++ b/src/2geom/2geom.h @@ -31,8 +31,8 @@ * the specific language governing rights and limitations. */ -#ifndef SEEN_LIB2GEOM_2GEOM_H -#define SEEN_LIB2GEOM_2GEOM_H +#ifndef LIB2GEOM_SEEN_2GEOM_H +#define LIB2GEOM_SEEN_2GEOM_H #include <2geom/forward.h> @@ -62,7 +62,7 @@ #include <2geom/math-utils.h> #include <2geom/utils.h> -#endif // SEEN_LIB2GEOM_HEADER_H +#endif // LIB2GEOM_SEEN_2GEOM_H /* Local Variables: mode:c++ diff --git a/src/2geom/CMakeLists.txt b/src/2geom/CMakeLists.txt index eeaecaa39..eb7abb614 100644 --- a/src/2geom/CMakeLists.txt +++ b/src/2geom/CMakeLists.txt @@ -2,23 +2,26 @@ set(2geom_SRC affine.cpp basic-intersection.cpp + bezier.cpp bezier-clipping.cpp bezier-curve.cpp bezier-utils.cpp - circle-circle.cpp + cairo-path-sink.cpp circle.cpp # conic_section_clipper_impl.cpp # conicsec.cpp - conjugate_gradient.cpp - convex-cover.cpp + convex-hull.cpp + coord.cpp crossing.cpp curve.cpp d2-sbasis.cpp ellipse.cpp elliptical-arc.cpp + elliptical-arc-from-sbasis.cpp geom.cpp + intersection-graph.cpp line.cpp - nearest-point.cpp + nearest-time.cpp numeric/matrix.cpp path-intersection.cpp path-sink.cpp @@ -26,11 +29,9 @@ set(2geom_SRC pathvector.cpp piecewise.cpp point.cpp - poly.cpp - quadtree.cpp + polynomial.cpp rect.cpp # recursive-bezier-intersection.cpp - region.cpp sbasis-2d.cpp sbasis-geometric.cpp sbasis-math.cpp @@ -38,16 +39,16 @@ set(2geom_SRC sbasis-roots.cpp sbasis-to-bezier.cpp sbasis.cpp - shape.cpp solve-bezier.cpp solve-bezier-one-d.cpp solve-bezier-parametric.cpp - svg-elliptical-arc.cpp svg-path-parser.cpp - sweep.cpp + svg-path-writer.cpp + sweep-bounds.cpp toposweep.cpp transforms.cpp utils.cpp + viewbox.cpp # ------- @@ -62,14 +63,11 @@ set(2geom_SRC bezier.h choose.h circle.h - circulator.h concepts.h conic_section_clipper.h conic_section_clipper_cr.h conic_section_clipper_impl.h conicsec.h - conjugate_gradient.h - convex-cover.h coord.h crossing.h curve.h @@ -83,7 +81,6 @@ set(2geom_SRC generic-interval.h generic-rect.h geom.h - hvlinesegment.h int-interval.h int-point.h int-rect.h @@ -91,20 +88,16 @@ set(2geom_SRC line.h linear.h math-utils.h - nearest-point.h ord.h path-intersection.h path-sink.h path.h pathvector.h piecewise.h - point-ops.h point.h - poly.h - quadtree.h + polynomial.h ray.h rect.h - region.h sbasis-2d.h sbasis-curve.h sbasis-geometric.h @@ -112,11 +105,11 @@ set(2geom_SRC sbasis-poly.h sbasis-to-bezier.h sbasis.h - shape.h solver.h - svg-elliptical-arc.h svg-path-parser.h - sweep.h + svg-path-writer.h + sweeper.h + sweep-bounds.h toposweep.h transforms.h utils.h diff --git a/src/2geom/Makefile_insert b/src/2geom/Makefile_insert index e77f413cb..e3c6836fd 100644 --- a/src/2geom/Makefile_insert +++ b/src/2geom/Makefile_insert @@ -15,12 +15,14 @@ 2geom/bezier-clipping.cpp \ 2geom/bezier-curve.cpp \ 2geom/bezier-curve.h \ + 2geom/bezier.cpp \ 2geom/bezier.h \ 2geom/bezier-to-sbasis.h \ 2geom/bezier-utils.cpp \ 2geom/bezier-utils.h \ + 2geom/cairo-path-sink.cpp \ + 2geom/cairo-path-sink.h \ 2geom/choose.h \ - 2geom/circle-circle.cpp \ 2geom/circle.cpp \ 2geom/circle.h \ 2geom/circulator.h \ @@ -32,10 +34,9 @@ 2geom/conic_section_clipper.h \ 2geom/conic_section_clipper_impl.cpp \ 2geom/conic_section_clipper_impl.h \ - 2geom/conjugate_gradient.cpp \ - 2geom/conjugate_gradient.h \ - 2geom/convex-cover.cpp \ - 2geom/convex-cover.h \ + 2geom/convex-hull.cpp \ + 2geom/convex-hull.h \ + 2geom/coord.cpp \ 2geom/coord.h \ 2geom/crossing.cpp \ 2geom/crossing.h \ @@ -49,13 +50,16 @@ 2geom/ellipse.h \ 2geom/elliptical-arc.cpp \ 2geom/elliptical-arc.h \ + 2geom/elliptical-arc-from-sbasis.cpp \ 2geom/exception.h \ 2geom/forward.h \ 2geom/generic-interval.h \ 2geom/generic-rect.h \ 2geom/geom.cpp \ 2geom/geom.h \ - 2geom/hvlinesegment.h \ + 2geom/intersection.h \ + 2geom/intersection-graph.cpp \ + 2geom/intersection-graph.h \ 2geom/interval.h \ 2geom/int-interval.h \ 2geom/int-point.h \ @@ -64,8 +68,8 @@ 2geom/line.cpp \ 2geom/line.h \ 2geom/math-utils.h \ - 2geom/nearest-point.cpp \ - 2geom/nearest-point.h \ + 2geom/nearest-time.cpp \ + 2geom/nearest-time.h \ 2geom/ord.h \ 2geom/path.cpp \ 2geom/path.h \ @@ -79,17 +83,12 @@ 2geom/piecewise.h \ 2geom/point.cpp \ 2geom/point.h \ - 2geom/point-ops.h \ - 2geom/poly.cpp \ - 2geom/poly.h \ - 2geom/quadtree.cpp \ - 2geom/quadtree.h \ + 2geom/polynomial.cpp \ + 2geom/polynomial.h \ 2geom/ray.h \ 2geom/rect.cpp \ 2geom/rect.h \ 2geom/recursive-bezier-intersection.cpp \ - 2geom/region.cpp \ - 2geom/region.h \ 2geom/sbasis-2d.cpp \ 2geom/sbasis-2d.h \ 2geom/sbasis.cpp \ @@ -104,24 +103,25 @@ 2geom/sbasis-roots.cpp \ 2geom/sbasis-to-bezier.cpp \ 2geom/sbasis-to-bezier.h \ - 2geom/shape.cpp \ - 2geom/shape.h \ 2geom/solve-bezier.cpp \ 2geom/solve-bezier-one-d.cpp \ 2geom/solve-bezier-parametric.cpp \ 2geom/solver.h \ - 2geom/svg-elliptical-arc.cpp \ - 2geom/svg-elliptical-arc.h \ 2geom/svg-path-parser.cpp \ 2geom/svg-path-parser.h \ - 2geom/sweep.cpp \ - 2geom/sweep.h \ + 2geom/svg-path-writer.cpp \ + 2geom/svg-path-writer.h \ + 2geom/sweep-bounds.cpp \ + 2geom/sweep-bounds.h \ + 2geom/sweeper.h \ 2geom/toposweep.cpp \ 2geom/toposweep.h \ 2geom/transforms.cpp \ 2geom/transforms.h \ 2geom/utils.cpp \ 2geom/utils.h \ + 2geom/viewbox.cpp \ + 2geom/viewbox.h \ 2geom/numeric/fitting-model.h \ 2geom/numeric/fitting-tool.h \ 2geom/numeric/linear_system.h \ diff --git a/src/2geom/affine.cpp b/src/2geom/affine.cpp index 738d0fc48..48179e864 100644 --- a/src/2geom/affine.cpp +++ b/src/2geom/affine.cpp @@ -6,9 +6,10 @@ * This code is in public domain */ -#include <2geom/utils.h> #include <2geom/affine.h> #include <2geom/point.h> +#include <2geom/polynomial.h> +#include <2geom/utils.h> namespace Geom { @@ -36,8 +37,7 @@ Point Affine::yAxis() const { return Point(_c[2], _c[3]); } -/** Gets the translation imparted by the Affine. - */ +/// Gets the translation imparted by the Affine. Point Affine::translation() const { return Point(_c[4], _c[5]); } @@ -52,8 +52,7 @@ void Affine::setYAxis(Point const &vec) { _c[i + 2] = vec[i]; } -/** Sets the translation imparted by the Affine. - */ +/// Sets the translation imparted by the Affine. void Affine::setTranslation(Point const &loc) { for(int i = 0; i < 2; i++) _c[i + 4] = loc[i]; @@ -61,33 +60,35 @@ void Affine::setTranslation(Point const &loc) { /** Calculates the amount of x-scaling imparted by the Affine. This is the scaling applied to * the original x-axis region. It is \emph{not} the overall x-scaling of the transformation. - * Equivalent to L2(m.xAxis()) - */ + * Equivalent to L2(m.xAxis()). */ double Affine::expansionX() const { return sqrt(_c[0] * _c[0] + _c[1] * _c[1]); } /** Calculates the amount of y-scaling imparted by the Affine. This is the scaling applied before * the other transformations. It is \emph{not} the overall y-scaling of the transformation. - * Equivalent to L2(m.yAxis()) - */ + * Equivalent to L2(m.yAxis()). */ double Affine::expansionY() const { return sqrt(_c[2] * _c[2] + _c[3] * _c[3]); } void Affine::setExpansionX(double val) { double exp_x = expansionX(); - if(!are_near(exp_x, 0.0)) { //TODO: best way to deal with it is to skip op? + if (exp_x != 0.0) { //TODO: best way to deal with it is to skip op? double coef = val / expansionX(); - for(unsigned i=0;i<2;i++) _c[i] *= coef; + for (unsigned i = 0; i < 2; ++i) { + _c[i] *= coef; + } } } void Affine::setExpansionY(double val) { double exp_y = expansionY(); - if(!are_near(exp_y, 0.0)) { //TODO: best way to deal with it is to skip op? + if (exp_y != 0.0) { //TODO: best way to deal with it is to skip op? double coef = val / expansionY(); - for(unsigned i=2; i<4; i++) _c[i] *= coef; + for (unsigned i = 2; i < 4; ++i) { + _c[i] *= coef; + } } } @@ -222,6 +223,29 @@ bool Affine::isNonzeroRotation(Coord eps) const { are_near(_c[0]*_c[0] + _c[1]*_c[1], 1.0, eps); } +/** @brief Check whether this matrix represents a non-zero rotation about any point. + * @param eps Numerical tolerance + * @return True iff the matrix is of the form + * \f$\left[\begin{array}{ccc} + a & b & 0 \\ + -b & a & 0 \\ + c & d & 1 \end{array}\right]\f$, \f$a^2 + b^2 = 1\f$ and \f$a \neq 1\f$. */ +bool Affine::isNonzeroNonpureRotation(Coord eps) const { + return !are_near(_c[0], 1.0, eps) && + are_near(_c[0], _c[3], eps) && are_near(_c[1], -_c[2], eps) && + are_near(_c[0]*_c[0] + _c[1]*_c[1], 1.0, eps); +} + +/** @brief For a (possibly non-pure) non-zero-rotation matrix, calculate the rotation center. + * @pre The matrix must be a non-zero-rotation matrix to prevent division by zero, see isNonzeroNonpureRotation(). + * @return The rotation center x, the solution to the equation + * \f$A x = x\f$. */ +Point Affine::rotationCenter() const { + Coord x = (_c[2]*_c[5]+_c[4]-_c[4]*_c[3]) / (1-_c[3]-_c[0]+_c[0]*_c[3]-_c[2]*_c[1]); + Coord y = (_c[1]*x + _c[5]) / (1 - _c[3]); + return Point(x,y); +}; + /** @brief Check whether this matrix represents pure horizontal shearing. * @param eps Numerical tolerance * @return True iff the matrix is of the form @@ -342,8 +366,7 @@ bool Affine::preservesDistances(Coord eps) const /** @brief Check whether this transformation flips objects. * A transformation flips objects if it has a negative scaling component. */ bool Affine::flips() const { - // TODO shouldn't this be det() < 0? - return cross(xAxis(), yAxis()) > 0; + return det() < 0; } /** @brief Check whether this matrix is singular. @@ -356,7 +379,7 @@ bool Affine::isSingular(Coord eps) const { } /** @brief Compute the inverse matrix. - * Inverse is a matrix (denoted \f$A^{-1}) such that \f$AA^{-1} = A^{-1}A = I\f$. + * Inverse is a matrix (denoted \f$A^{-1}\f$) such that \f$AA^{-1} = A^{-1}A = I\f$. * Singular matrices have no inverse (for example a matrix that has two of its columns equal). * For such matrices, the identity matrix will be returned instead. * @param eps Numerical tolerance @@ -369,7 +392,7 @@ Affine Affine::inverse() const { fabs(_c[2]) + fabs(_c[3])); // a random matrix norm (either l1 or linfty if(mx > 0) { Geom::Coord const determ = det(); - if (!rel_error_bound(determ, mx*mx)) { + if (!rel_error_bound(std::sqrt(fabs(determ)), mx)) { Geom::Coord const ideterm = 1.0 / (determ); d._c[0] = _c[3] * ideterm; @@ -446,55 +469,38 @@ Affine elliptic_quadratic_form(Affine const &m) { Eigen::Eigen(Affine const &m) { double const B = -m[0] - m[3]; double const C = m[0]*m[3] - m[1]*m[2]; - double const center = -B/2.0; - double const delta = sqrt(B*B-4*C)/2.0; - values[0] = center + delta; values[1] = center - delta; - for (int i = 0; i < 2; i++) { - vectors[i] = unit_vector(rot90(Point(m[0]-values[i], m[1]))); - } -} -static void quadratic_roots(const double q0, const double q1, const double q2, int &n, double&r0, double&r1) { - if(q2 == 0) { - if(q1 == 0) { // zero or infinite roots - n = 0; - } else { - n = 1; - r0 = -q0/q1; - } - } else { - double desc = q1*q1 - 4*q2*q0; - if (desc < 0) - n = 0; - else if (desc == 0) { - n = 1; - r0 = -q1/(2*q2); - } else { - n = 2; - desc = std::sqrt(desc); - double t = -0.5*(q1+sgn(q1)*desc); - r0 = t/q2; - r1 = q0/t; - } + std::vector<double> v = solve_quadratic(1, B, C); + + for (unsigned i = 0; i < v.size(); ++i) { + values[i] = v[i]; + vectors[i] = unit_vector(rot90(Point(m[0] - values[i], m[1]))); + } + for (unsigned i = v.size(); i < 2; ++i) { + values[i] = 0; + vectors[i] = Point(0,0); } } Eigen::Eigen(double m[2][2]) { double const B = -m[0][0] - m[1][1]; double const C = m[0][0]*m[1][1] - m[1][0]*m[0][1]; - //double const desc = B*B-4*C; - //double t = -0.5*(B+sgn(B)*desc); - int n; - values[0] = values[1] = 0; - quadratic_roots(C, B, 1, n, values[0], values[1]); - for (int i = 0; i < n; i++) - vectors[i] = unit_vector(rot90(Point(m[0][0]-values[i], m[0][1]))); - for (int i = n; i < 2; i++) + + std::vector<double> v = solve_quadratic(1, B, C); + + for (unsigned i = 0; i < v.size(); ++i) { + values[i] = v[i]; + vectors[i] = unit_vector(rot90(Point(m[0][0] - values[i], m[0][1]))); + } + for (unsigned i = v.size(); i < 2; ++i) { + values[i] = 0; vectors[i] = Point(0,0); + } } -/** @brief Nearness predicate for affine transforms - * @returns True if all entries of matrices are within eps of each other */ +/** @brief Nearness predicate for affine transforms. + * @returns True if all entries of matrices are within eps of each other. + * @relates Affine */ bool are_near(Affine const &a, Affine const &b, Coord eps) { return are_near(a[0], b[0], eps) && are_near(a[1], b[1], eps) && diff --git a/src/2geom/affine.h b/src/2geom/affine.h index af7b39360..470d5fc40 100644 --- a/src/2geom/affine.h +++ b/src/2geom/affine.h @@ -11,8 +11,8 @@ * This code is in public domain. */ -#ifndef SEEN_LIB2GEOM_AFFINE_H -#define SEEN_LIB2GEOM_AFFINE_H +#ifndef LIB2GEOM_SEEN_AFFINE_H +#define LIB2GEOM_SEEN_AFFINE_H #include <boost/operators.hpp> #include <2geom/forward.h> @@ -150,6 +150,8 @@ public: bool isNonzeroScale(Coord eps = EPSILON) const; bool isNonzeroUniformScale(Coord eps = EPSILON) const; bool isNonzeroRotation(Coord eps = EPSILON) const; + bool isNonzeroNonpureRotation(Coord eps = EPSILON) const; + Point rotationCenter() const; bool isNonzeroHShear(Coord eps = EPSILON) const; bool isNonzeroVShear(Coord eps = EPSILON) const; diff --git a/src/2geom/angle.h b/src/2geom/angle.h index 1faf63c3f..af4442e88 100644 --- a/src/2geom/angle.h +++ b/src/2geom/angle.h @@ -59,32 +59,48 @@ namespace Geom { * to <tt>double</tt> is in the range \f$[-\pi, \pi)\f$ - the convention used by C's * math library. * + * This class holds only a single floating point value, so passing it by value will generally + * be faster than passing it by const reference. + * * @ingroup Primitives */ class Angle : boost::additive< Angle + , boost::additive< Angle, Coord , boost::equality_comparable< Angle - > > + , boost::equality_comparable< Angle, Coord + > > > > { public: - Angle() : _angle(0) {} //added default constructor because of cython + Angle() : _angle(0) {} Angle(Coord v) : _angle(v) { _normalize(); } // this can be called implicitly - explicit Angle(Point p) : _angle(atan2(p)) { _normalize(); } - Angle(Point a, Point b) : _angle(angle_between(a, b)) { _normalize(); } + explicit Angle(Point const &p) : _angle(atan2(p)) { _normalize(); } + Angle(Point const &a, Point const &b) : _angle(angle_between(a, b)) { _normalize(); } operator Coord() const { return radians(); } - Angle &operator+=(Angle const &o) { + Angle &operator+=(Angle o) { _angle += o._angle; _normalize(); return *this; } - Angle &operator-=(Angle const &o) { + Angle &operator-=(Angle o) { _angle -= o._angle; _normalize(); return *this; } - bool operator==(Angle const &o) const { + Angle &operator+=(Coord a) { + *this += Angle(a); + return *this; + } + Angle &operator-=(Coord a) { + *this -= Angle(a); + return *this; + } + bool operator==(Angle o) const { return _angle == o._angle; } + bool operator==(Coord c) const { + return _angle == Angle(c)._angle; + } /** @brief Get the angle as radians. * @return Number in range \f$[-\pi, \pi)\f$. */ @@ -136,12 +152,22 @@ public: private: void _normalize() { - _angle -= floor(_angle * (1.0/(2*M_PI))) * 2*M_PI; + _angle = std::fmod(_angle, 2*M_PI); + if (_angle < 0) _angle += 2*M_PI; + //_angle -= floor(_angle * (1.0/(2*M_PI))) * 2*M_PI; } Coord _angle; // this is always in [0, 2pi) friend class AngleInterval; }; +inline Angle distance(Angle const &a, Angle const &b) { + // the distance cannot be larger than M_PI. + Coord ac = a.radians0(); + Coord bc = b.radians0(); + Coord d = fabs(ac - bc); + return Angle(d > M_PI ? 2*M_PI - d : d); +} + /** @brief Directed angular interval. * * Wrapper for directed angles with defined start and end values. Useful e.g. for representing @@ -149,50 +175,156 @@ private: * in the interval (it is a closed interval). Angular intervals can also be interptered * as functions \f$f: [0, 1] \to [-\pi, \pi)\f$, which return the start angle for 0, * the end angle for 1, and interpolate linearly for other values. Note that such functions - * are not continuous if the interval contains the zero angle. + * are not continuous if the interval crosses the angle \f$\pi\f$. * - * This class is immutable - you cannot change the values of start and end angles - * without creating a new instance of this class. + * This class can represent all directed angular intervals, including empty ones. + * However, not all possible intervals can be created with the constructors. + * For full control, use the setInitial(), setFinal() and setAngles() methods. * * @ingroup Primitives */ -class AngleInterval { +class AngleInterval + : boost::equality_comparable< AngleInterval > +{ public: - AngleInterval(Angle const &s, Angle const &e, bool cw = false) - : _start_angle(s), _end_angle(e), _sweep(cw) + AngleInterval() {} + /** @brief Create an angular interval from two angles and direction. + * If the initial and final angle are the same, a degenerate interval + * (containing only one angle) will be created. + * @param s Starting angle + * @param e Ending angle + * @param cw Which direction the interval goes. True means that it goes + * in the direction of increasing angles, while false means in the direction + * of decreasing angles. */ + AngleInterval(Angle s, Angle e, bool cw = false) + : _start_angle(s), _end_angle(e), _sweep(cw), _full(false) {} AngleInterval(double s, double e, bool cw = false) - : _start_angle(s), _end_angle(e), _sweep(cw) + : _start_angle(s), _end_angle(e), _sweep(cw), _full(false) {} - /** @brief Get the angular coordinate of the interval's initial point - * @return Angle in range \f$[0,2\pi)\f$ corresponding to the start of arc */ - Angle const &initialAngle() const { return _start_angle; } - /** @brief Get the angular coordinate of the interval's final point - * @return Angle in range \f$[0,2\pi)\f$ corresponding to the end of arc */ - Angle const &finalAngle() const { return _end_angle; } - bool isDegenerate() const { return initialAngle() == finalAngle(); } - /** @brief Get an angle corresponding to the specified time value. */ + /** @brief Create an angular interval from three angles. + * If the inner angle is exactly equal to initial or final angle, + * the sweep flag will be set to true, i.e. the interval will go + * in the direction of increasing angles. + * + * If the initial and final angle are the same, but the inner angle + * is different, a full angle in the direction of increasing angles + * will be created. + * + * @param s Initial angle + * @param inner Angle contained in the interval + * @param e Final angle */ + AngleInterval(Angle s, Angle inner, Angle e) + : _start_angle(s) + , _end_angle(e) + , _sweep((inner-s).radians0() <= (e-s).radians0()) + , _full(s == e && s != inner) + { + if (_full) { + _sweep = true; + } + } + + /// Get the start angle. + Angle initialAngle() const { return _start_angle; } + /// Get the end angle. + Angle finalAngle() const { return _end_angle; } + /// Check whether the interval goes in the direction of increasing angles. + bool sweep() const { return _sweep; } + /// Check whether the interval contains only a single angle. + bool isDegenerate() const { + return _start_angle == _end_angle && !_full; + } + /// Check whether the interval contains all angles. + bool isFull() const { + return _start_angle == _end_angle && _full; + } + + /** @brief Set the initial angle. + * @param a Angle to set + * @param prefer_full Whether to set a full angular interval when + * the initial angle is set to the final angle */ + void setInitial(Angle a, bool prefer_full = false) { + _start_angle = a; + _full = prefer_full && a == _end_angle; + } + + /** @brief Set the final angle. + * @param a Angle to set + * @param prefer_full Whether to set a full angular interval when + * the initial angle is set to the final angle */ + void setFinal(Angle a, bool prefer_full = false) { + _end_angle = a; + _full = prefer_full && a == _start_angle; + } + /** @brief Set both angles at once. + * The direction (sweep flag) is left unchanged. + * @param s Initial angle + * @param e Final angle + * @param prefer_full Whether to set a full interval when the passed + * initial and final angle are the same */ + void setAngles(Angle s, Angle e, bool prefer_full = false) { + _start_angle = s; + _end_angle = e; + _full = prefer_full && s == e; + } + /// Set whether the interval goes in the direction of increasing angles. + void setSweep(bool s) { _sweep = s; } + + /// Reverse the direction of the interval while keeping contained values the same. + void reverse() { + using std::swap; + swap(_start_angle, _end_angle); + _sweep = !_sweep; + } + /// Get a new interval with reversed direction. + AngleInterval reversed() const { + AngleInterval result(*this); + result.reverse(); + return result; + } + + /// Get an angle corresponding to the specified time value. Angle angleAt(Coord t) const { Coord span = extent(); Angle ret = _start_angle.radians0() + span * (_sweep ? t : -t); return ret; } Angle operator()(Coord t) const { return angleAt(t); } -#if 0 - /** @brief Find an angle nearest to the specified time value. - * @param a Query angle - * @return If the interval contains the query angle, a number from the range [0, 1] - * such that a = angleAt(t); otherwise, 0 or 1, depending on which extreme - * angle is nearer. */ - Coord nearestAngle(Angle const &a) const { - Coord dist = distance(_start_angle, a, _sweep).radians0(); - Coord span = distance(_start_angle, _end_angle, _sweep).radians0(); - if (dist <= span) return dist / span; - else return distance_abs(_start_angle, a).radians0() > distance_abs(_end_angle, a).radians0() ? 1.0 : 0.0; + + /** @brief Compute a time value that would evaluate to the given angle. + * If the start and end angle are exactly the same, NaN will be returned. + * Negative values will be returned for angles between the initial angle + * and the angle exactly opposite the midpoint of the interval. */ + Coord timeAtAngle(Angle a) const { + if (_full) { + Angle ta = _sweep ? a - _start_angle : _start_angle - a; + return ta.radians0() / (2*M_PI); + } + Coord ex = extent(); + Coord outex = 2*M_PI - ex; + if (_sweep) { + Angle midout = _start_angle - outex / 2; + Angle acmp = a - midout, scmp = _start_angle - midout; + if (acmp.radians0() >= scmp.radians0()) { + return (a - _start_angle).radians0() / ex; + } else { + return -(_start_angle - a).radians0() / ex; + } + } else { + Angle midout = _start_angle + outex / 2; + Angle acmp = a - midout, scmp = _start_angle - midout; + if (acmp.radians0() <= scmp.radians0()) { + return (_start_angle - a).radians0() / ex; + } else { + return -(a - _start_angle).radians0() / ex; + } + } } -#endif - /** @brief Check whether the interval includes the given angle. */ - bool contains(Angle const &a) const { + + /// Check whether the interval includes the given angle. + bool contains(Angle a) const { + if (_full) return true; Coord s = _start_angle.radians0(); Coord e = _end_angle.radians0(); Coord x = a.radians0(); @@ -205,18 +337,48 @@ public: } } /** @brief Extent of the angle interval. - * @return Extent in range \f$[0, 2\pi)\f$ */ + * Equivalent to the absolute value of the sweep angle. + * @return Extent in range \f$[0, 2\pi)\f$. */ Coord extent() const { - Coord d = _end_angle - _start_angle; - if (!_sweep) d = -d; - if (d < 0) d += 2*M_PI; - return d; + if (_full) return 2*M_PI; + return _sweep + ? (_end_angle - _start_angle).radians0() + : (_start_angle - _end_angle).radians0(); } -protected: - AngleInterval() {} + /** @brief Get the sweep angle of the interval. + * This is the value you need to add to the initial angle to get the final angle. + * It is positive when sweep is true. Denoted as \f$\Delta\theta\f$ in the SVG + * elliptical arc implementation notes. */ + Coord sweepAngle() const { + if (_full) return _sweep ? 2*M_PI : -2*M_PI; + Coord sa = _end_angle.radians0() - _start_angle.radians0(); + if (_sweep && sa < 0) sa += 2*M_PI; + if (!_sweep && sa > 0) sa -= 2*M_PI; + return sa; + } + + /// Check another interval for equality. + bool operator==(AngleInterval const &other) const { + if (_start_angle != other._start_angle) return false; + if (_end_angle != other._end_angle) return false; + if (_sweep != other._sweep) return false; + if (_full != other._full) return false; + return true; + } + + static AngleInterval create_full(Angle start, bool sweep = true) { + AngleInterval result; + result._start_angle = result._end_angle = start; + result._sweep = sweep; + result._full = true; + return result; + } + +private: Angle _start_angle; Angle _end_angle; bool _sweep; + bool _full; }; /** @brief Given an angle in degrees, return radians @@ -226,88 +388,12 @@ inline Coord deg_to_rad(Coord deg) { return deg*M_PI/180.0;} * @relates Angle */ inline Coord rad_to_deg(Coord rad) { return rad*180.0/M_PI;} -/* - * start_angle and angle must belong to [0, 2PI[ - * and angle must belong to the cirsular arc defined by - * start_angle, end_angle and with rotation direction cw - */ -inline -double map_circular_arc_on_unit_interval( double angle, double start_angle, double end_angle, bool cw = true ) -{ - double d = end_angle - start_angle; - double t = angle - start_angle; - if ( !cw ) - { - d = -d; - t = -t; - } - d = std::fmod(d, 2*M_PI); - t = std::fmod(t, 2*M_PI); - if ( d < 0 ) d += 2*M_PI; - if ( t < 0 ) t += 2*M_PI; - return t / d; -} - -inline -Coord map_unit_interval_on_circular_arc(Coord t, double start_angle, double end_angle, bool cw = true) -{ - double sweep_angle = end_angle - start_angle; - if ( !cw ) sweep_angle = -sweep_angle; - sweep_angle = std::fmod(sweep_angle, 2*M_PI); - if ( sweep_angle < 0 ) sweep_angle += 2*M_PI; - - Coord angle = start_angle; - if ( cw ) - { - angle += sweep_angle * t; - } - else - { - angle -= sweep_angle * t; - } - angle = std::fmod(angle, 2*M_PI); - if (angle < 0) angle += 2*M_PI; - return angle; -} - -/* - * Return true if the given angle is contained in the circular arc determined - * by the passed angles. - * - * a: the angle to be tested - * sa: the angle the arc start from - * ia: an angle strictly inner to the arc - * ea: the angle the arc end to - * - * prerequisite: the inner angle has to be not equal (mod 2PI) to the start - * or the end angle, except when they are equal each other, too. - * warning: when sa == ea (mod 2PI) they define a whole circle - * if ia != sa (mod 2PI), on the contrary if ia == sa == ea (mod 2PI) - * they define a single point. - */ -inline -bool arc_contains (double a, double sa, double ia, double ea) -{ - a -= sa; - a = std::fmod(a, 2*M_PI); - if (a < 0) a += 2*M_PI; - ia -= sa; - ia = std::fmod(ia, 2*M_PI); - if (ia < 0) ia += 2*M_PI; - ea -= sa; - ea = std::fmod(ea, 2*M_PI); - if (ea < 0) ea += 2*M_PI; +} // end namespace Geom - if (ia == 0 && ea == 0) return (a == 0); - if (ia == 0 || ia == ea) - { - THROW_RANGEERROR ("arc_contains: passed angles do not define an arc"); - } - return (ia < ea && a <= ea) || (ia > ea && (a >= ea || a == 0)); +namespace std { +template <> class iterator_traits<Geom::Angle> {}; } -} // end namespace Geom - #endif // LIB2GEOM_SEEN_ANGLE_H /* diff --git a/src/2geom/basic-intersection.cpp b/src/2geom/basic-intersection.cpp index 379ec597c..54374e282 100644 --- a/src/2geom/basic-intersection.cpp +++ b/src/2geom/basic-intersection.cpp @@ -1,3 +1,38 @@ +/** @file + * @brief Basic intersection routines + *//* + * Authors: + * Nathan Hurst <njh@njhurst.com> + * Marco Cecchetti <mrcekets at gmail.com> + * Jean-François Barraud <jf.barraud@gmail.com> + * + * Copyright 2008-2009 Authors + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + */ + #include <2geom/basic-intersection.h> #include <2geom/sbasis-to-bezier.h> #include <2geom/exception.h> @@ -33,24 +68,35 @@ namespace Geom { //#else namespace detail{ namespace bezier_clipping { -void portion (std::vector<Point> & B, Interval const& I); +void portion(std::vector<Point> &B, Interval const &I); +void derivative(std::vector<Point> &D, std::vector<Point> const &B); }; }; void find_intersections(std::vector<std::pair<double, double> > &xs, + D2<Bezier> const & A, + D2<Bezier> const & B, + double precision) +{ + find_intersections_bezier_clipping(xs, bezier_points(A), bezier_points(B), precision); +} + +void find_intersections(std::vector<std::pair<double, double> > &xs, D2<SBasis> const & A, - D2<SBasis> const & B) { + D2<SBasis> const & B, + double precision) +{ vector<Point> BezA, BezB; sbasis_to_bezier(BezA, A); sbasis_to_bezier(BezB, B); - - xs.clear(); - find_intersections_bezier_clipping(xs, BezA, BezB); + find_intersections_bezier_clipping(xs, BezA, BezB, precision); } + void find_intersections(std::vector< std::pair<double, double> > & xs, - std::vector<Point> const& A, - std::vector<Point> const& B, - double precision){ + std::vector<Point> const& A, + std::vector<Point> const& B, + double precision) +{ find_intersections_bezier_clipping(xs, A, B, precision); } @@ -61,6 +107,7 @@ void find_intersections(std::vector< std::pair<double, double> > & xs, * Temporary storage is minimized by using part of the storage for the result * to hold an intermediate value until it is no longer needed. */ +// TODO replace with Bezier method void split(vector<Point> const &p, double t, vector<Point> &left, vector<Point> &right) { const unsigned sz = p.size(); @@ -88,44 +135,48 @@ void split(vector<Point> const &p, double t, } -void -find_self_intersections(std::vector<std::pair<double, double> > &xs, - D2<SBasis> const & A) { - vector<double> dr = roots(derivative(A[X])); + +void find_self_intersections(std::vector<std::pair<double, double> > &xs, + D2<Bezier> const &A, + double precision) +{ + std::vector<double> dr = derivative(A[X]).roots(); { - vector<double> dyr = roots(derivative(A[Y])); + std::vector<double> dyr = derivative(A[Y]).roots(); dr.insert(dr.begin(), dyr.begin(), dyr.end()); } dr.push_back(0); dr.push_back(1); // We want to be sure that we have no empty segments - sort(dr.begin(), dr.end()); - vector<double>::iterator new_end = unique(dr.begin(), dr.end()); + std::sort(dr.begin(), dr.end()); + std::vector<double>::iterator new_end = std::unique(dr.begin(), dr.end()); dr.resize( new_end - dr.begin() ); - vector<vector<Point> > pieces; - { - vector<Point> in, l, r; - sbasis_to_bezier(in, A); + std::vector< D2<Bezier> > pieces; + for (unsigned i = 0; i < dr.size() - 1; ++i) { + pieces.push_back(portion(A, dr[i], dr[i+1])); + } + /*{ + vector<Point> l, r, in = A; for(unsigned i = 0; i < dr.size()-1; i++) { split(in, (dr[i+1]-dr[i]) / (1 - dr[i]), l, r); pieces.push_back(l); in = r; } - } + }*/ for(unsigned i = 0; i < dr.size()-1; i++) { for(unsigned j = i+1; j < dr.size()-1; j++) { std::vector<std::pair<double, double> > section; - find_intersections( section, pieces[i], pieces[j]); + find_intersections(section, pieces[i], pieces[j], precision); for(unsigned k = 0; k < section.size(); k++) { double l = section[k].first; double r = section[k].second; // XXX: This condition will prune out false positives, but it might create some false negatives. Todo: Confirm it is correct. if(j == i+1) //if((l == 1) && (r == 0)) - if( ( l > 1-1e-4 ) && (r < 1e-4) )//FIXME: what precision should be used here??? + if( ( l > precision ) && (r < precision) )//FIXME: what precision should be used here??? continue; xs.push_back(std::make_pair((1-l)*dr[i] + l*dr[i+1], (1-r)*dr[j] + r*dr[j+1])); @@ -138,6 +189,46 @@ find_self_intersections(std::vector<std::pair<double, double> > &xs, //unique(xs.begin(), xs.end()); } +void find_self_intersections(std::vector<std::pair<double, double> > &xs, + D2<SBasis> const &A, + double precision) +{ + D2<Bezier> in; + sbasis_to_bezier(in, A); + find_self_intersections(xs, in, precision); +} + + +void subdivide(D2<Bezier> const &a, + D2<Bezier> const &b, + std::vector< std::pair<double, double> > const &xs, + std::vector< D2<Bezier> > &av, + std::vector< D2<Bezier> > &bv) +{ + if (xs.empty()) { + av.push_back(a); + bv.push_back(b); + return; + } + + std::pair<double, double> prev = std::make_pair(0., 0.); + for (unsigned i = 0; i < xs.size(); ++i) { + av.push_back(portion(a, prev.first, xs[i].first)); + bv.push_back(portion(b, prev.second, xs[i].second)); + av.back()[X].at0() = bv.back()[X].at0() = lerp(0.5, av.back()[X].at0(), bv.back()[X].at0()); + av.back()[X].at1() = bv.back()[X].at1() = lerp(0.5, av.back()[X].at1(), bv.back()[X].at1()); + av.back()[Y].at0() = bv.back()[Y].at0() = lerp(0.5, av.back()[Y].at0(), bv.back()[Y].at0()); + av.back()[Y].at1() = bv.back()[Y].at1() = lerp(0.5, av.back()[Y].at1(), bv.back()[Y].at1()); + prev = xs[i]; + } + av.push_back(portion(a, prev.first, 1)); + bv.push_back(portion(b, prev.second, 1)); + av.back()[X].at0() = bv.back()[X].at0() = lerp(0.5, av.back()[X].at0(), bv.back()[X].at0()); + av.back()[X].at1() = bv.back()[X].at1() = lerp(0.5, av.back()[X].at1(), bv.back()[X].at1()); + av.back()[Y].at0() = bv.back()[Y].at0() = lerp(0.5, av.back()[Y].at0(), bv.back()[Y].at0()); + av.back()[Y].at1() = bv.back()[Y].at1() = lerp(0.5, av.back()[Y].at1(), bv.back()[Y].at1()); +} + #ifdef HAVE_GSL #include <gsl/gsl_multiroots.h> @@ -304,38 +395,6 @@ void polish_intersections(std::vector<std::pair<double, double> > &xs, B, xs[i].second); } - - /** - * Compute the Hausdorf distance from A to B only. - */ - - -#if 0 -/** Compute the value of a bezier - Todo: find a good palce for this. - */ -// suggested by Sederberg. -Point OldBezier::operator()(double t) const { - int n = p.size()-1; - double u, bc, tn, tmp; - int i; - Point r; - for(int dim = 0; dim < 2; dim++) { - u = 1.0 - t; - bc = 1; - tn = 1; - tmp = p[0][dim]*u; - for(i=1; i<n; i++){ - tn = tn*t; - bc = bc*(n-i+1)/i; - tmp = (tmp + tn*bc*p[i][dim])*u; - } - r[dim] = (tmp + tn*t*p[n][dim]); - } - return r; -} -#endif - /** * Compute the Hausdorf distance from A to B only. */ @@ -350,7 +409,7 @@ double hausdorfl(D2<SBasis>& A, D2<SBasis> const& B, double h_dist = 0, h_a_t = 0, h_b_t = 0; double dist = 0; Point Ax = A.at0(); - double t = Geom::nearest_point(Ax, B); + double t = Geom::nearest_time(Ax, B); dist = Geom::distance(Ax, B(t)); if (dist > h_dist) { h_a_t = 0; @@ -358,7 +417,7 @@ double hausdorfl(D2<SBasis>& A, D2<SBasis> const& B, h_dist = dist; } Ax = A.at1(); - t = Geom::nearest_point(Ax, B); + t = Geom::nearest_time(Ax, B); dist = Geom::distance(Ax, B(t)); if (dist > h_dist) { h_a_t = 1; @@ -370,7 +429,7 @@ double hausdorfl(D2<SBasis>& A, D2<SBasis> const& B, Point At = A(xs[i].first); Point Bu = B(xs[i].second); double distAtBu = Geom::distance(At, Bu); - t = Geom::nearest_point(At, B); + t = Geom::nearest_time(At, B); dist = Geom::distance(At, B(t)); //FIXME: we might miss it due to floating point precision... if (dist >= distAtBu-.1 && distAtBu > h_dist) { @@ -396,7 +455,7 @@ double hausdorf(D2<SBasis>& A, D2<SBasis> const& B, double dist = 0; Point Bx = B.at0(); - double t = Geom::nearest_point(Bx, A); + double t = Geom::nearest_time(Bx, A); dist = Geom::distance(Bx, A(t)); if (dist > h_dist) { if(a_t) *a_t = t; @@ -404,7 +463,7 @@ double hausdorf(D2<SBasis>& A, D2<SBasis> const& B, h_dist = dist; } Bx = B.at1(); - t = Geom::nearest_point(Bx, A); + t = Geom::nearest_time(Bx, A); dist = Geom::distance(Bx, A(t)); if (dist > h_dist) { if(a_t) *a_t = t; diff --git a/src/2geom/basic-intersection.h b/src/2geom/basic-intersection.h index 5a813ae99..0b0bf8930 100644 --- a/src/2geom/basic-intersection.h +++ b/src/2geom/basic-intersection.h @@ -1,11 +1,12 @@ -/** - * \file - * \brief Basic intersection routines - * +/** @file + * @brief Basic intersection routines + *//* * Authors: - * ? <?@?.?> + * Nathan Hurst <njh@njhurst.com> + * Marco Cecchetti <mrcekets at gmail.com> + * Jean-François Barraud <jf.barraud@gmail.com> * - * Copyright ?-? authors + * Copyright 2008-2009 Authors * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public @@ -32,10 +33,11 @@ * */ -#ifndef SEEN_GEOM_BASICINTERSECTION_H -#define SEEN_GEOM_BASICINTERSECTION_H +#ifndef LIB2GEOM_SEEN_BASIC_INTERSECTION_H +#define LIB2GEOM_SEEN_BASIC_INTERSECTION_H #include <2geom/point.h> +#include <2geom/bezier.h> #include <2geom/sbasis.h> #include <2geom/d2.h> @@ -47,41 +49,29 @@ namespace Geom { -//why not allowing precision to be set here? -void find_intersections(std::vector<std::pair<double, double> >& xs, - D2<SBasis> const & A, - D2<SBasis> const & B); +void find_intersections(std::vector<std::pair<double, double> > &xs, + D2<Bezier> const &A, + D2<Bezier> const &B, + double precision = EPSILON); + +void find_intersections(std::vector<std::pair<double, double> > &xs, + D2<SBasis> const &A, + D2<SBasis> const &B, + double precision = EPSILON); + +void find_intersections(std::vector< std::pair<double, double> > &xs, + std::vector<Point> const &A, + std::vector<Point> const &B, + double precision = EPSILON); + +void find_self_intersections(std::vector<std::pair<double, double> > &xs, + D2<SBasis> const &A, + double precision = EPSILON); + +void find_self_intersections(std::vector<std::pair<double, double> > &xs, + D2<Bezier> const &A, + double precision = EPSILON); -void find_intersections(std::vector< std::pair<double, double> > & xs, - std::vector<Point> const& A, - std::vector<Point> const& B, - double precision = 1e-5); - -//why not allowing precision to be set here? -void find_self_intersections(std::vector<std::pair<double, double> >& xs, - D2<SBasis> const & A); - - -//--not implemented -//void find_self_intersections(std::vector<std::pair<double, double> >& xs, -// std::vector<Point> const & A); - - -//TODO: this should be moved to .cpp, shouldn't it? -// #ifdef USE_RECURSIVE_INTERSECTOR -// /* -// * find_intersection -// * -// * input: A, B - set of control points of two Bezier curve -// * input: precision - required precision of computation -// * output: xs - set of pairs of parameter values -// * at which crossing happens -// */ -// void find_intersections_bezier_recursive (std::vector< std::pair<double, double> > & xs, -// std::vector<Point> const& A, -// std::vector<Point> const& B, -// double precision = 1e-5); -// #else /* * find_intersection * @@ -96,10 +86,14 @@ void find_self_intersections(std::vector<std::pair<double, double> >& xs, void find_intersections_bezier_clipping (std::vector< std::pair<double, double> > & xs, std::vector<Point> const& A, std::vector<Point> const& B, - double precision = 1e-5); + double precision = EPSILON); //#endif - +void subdivide(D2<Bezier> const &a, + D2<Bezier> const &b, + std::vector< std::pair<double, double> > const &xs, + std::vector< D2<Bezier> > &av, + std::vector< D2<Bezier> > &bv); /* * find_collinear_normal @@ -115,7 +109,7 @@ void find_intersections_bezier_clipping (std::vector< std::pair<double, double> void find_collinear_normal (std::vector< std::pair<double, double> >& xs, std::vector<Point> const& A, std::vector<Point> const& B, - double precision = 1e-5); + double precision = EPSILON); void polish_intersections(std::vector<std::pair<double, double> > &xs, D2<SBasis> const &A, @@ -125,19 +119,19 @@ void polish_intersections(std::vector<std::pair<double, double> > &xs, /** * Compute the Hausdorf distance from A to B only. */ -double hausdorfl(D2<SBasis>& A, D2<SBasis> const& B, +double hausdorfl(D2<SBasis>& A, D2<SBasis> const &B, double m_precision, - double *a_t=0, double* b_t=0); + double *a_t=NULL, double *b_t=NULL); /** * Compute the symmetric Hausdorf distance. */ -double hausdorf(D2<SBasis>& A, D2<SBasis> const& B, +double hausdorf(D2<SBasis> &A, D2<SBasis> const &B, double m_precision, - double *a_t=0, double* b_t=0); + double *a_t=NULL, double *b_t=NULL); } -#endif // !SEEN_GEOM_BASICINTERSECTION_H +#endif // !LIB2GEOM_SEEN_BASIC_INTERSECTION_H /* Local Variables: diff --git a/src/2geom/bezier-clipping.cpp b/src/2geom/bezier-clipping.cpp index 9a055204f..be8dd5a5f 100644 --- a/src/2geom/bezier-clipping.cpp +++ b/src/2geom/bezier-clipping.cpp @@ -39,8 +39,9 @@ #include <2geom/point.h> #include <2geom/interval.h> #include <2geom/bezier.h> -//#include <2geom/convex-cover.h> #include <2geom/numeric/matrix.h> +#include <2geom/convex-hull.h> +#include <2geom/line.h> #include <cassert> #include <vector> @@ -48,7 +49,7 @@ #include <utility> //#include <iomanip> - +using std::swap; #define VERBOSE 0 @@ -63,7 +64,6 @@ namespace detail { namespace bezier_clipping { // for debugging // -inline void print(std::vector<Point> const& cp, const char* msg = "") { std::cerr << msg << std::endl; @@ -72,7 +72,6 @@ void print(std::vector<Point> const& cp, const char* msg = "") } template< class charT > -inline std::basic_ostream<charT> & operator<< (std::basic_ostream<charT> & os, const Interval & I) { @@ -80,7 +79,6 @@ operator<< (std::basic_ostream<charT> & os, const Interval & I) return os; } -inline double angle (std::vector<Point> const& A) { size_t n = A.size() -1; @@ -88,7 +86,6 @@ double angle (std::vector<Point> const& A) return (180 * a / M_PI); } -inline size_t get_precision(Interval const& I) { double d = I.extent(); @@ -103,7 +100,6 @@ size_t get_precision(Interval const& I) return n; } -inline void range_assertion(int k, int m, int n, const char* msg) { if ( k < m || k > n) @@ -118,92 +114,11 @@ void range_assertion(int k, int m, int n, const char* msg) //////////////////////////////////////////////////////////////////////////////// -// convex hull - -/* - * return true in case the oriented polyline p0, p1, p2 is a right turn - */ -inline -bool is_a_right_turn (Point const& p0, Point const& p1, Point const& p2) -{ - if (p1 == p2) return false; - Point q1 = p1 - p0; - Point q2 = p2 - p0; - if (q1 == -q2) return false; - return (cross (q1, q2) < 0); -} - -/* - * return true if p < q wrt the lexicographyc order induced by the coordinates - */ -struct lex_less -{ - bool operator() (Point const& p, Point const& q) - { - return ((p[X] < q[X]) || (p[X] == q[X] && p[Y] < q[Y])); - } -}; - -/* - * return true if p > q wrt the lexicographyc order induced by the coordinates - */ -struct lex_greater -{ - bool operator() (Point const& p, Point const& q) - { - return ((p[X] > q[X]) || (p[X] == q[X] && p[Y] > q[Y])); - } -}; - -/* - * Compute the convex hull of a set of points. - * The implementation is based on the Andrew's scan algorithm - * note: in the Bezier clipping for collinear normals it seems - * to be more stable wrt the Graham's scan algorithm and in general - * a bit quikier - */ -void convex_hull (std::vector<Point> & P) -{ - size_t n = P.size(); - if (n < 2) return; - std::sort(P.begin(), P.end(), lex_less()); - if (n < 4) return; - // upper hull - size_t u = 2; - for (size_t i = 2; i < n; ++i) - { - while (u > 1 && !is_a_right_turn(P[u-2], P[u-1], P[i])) - { - --u; - } - std::swap(P[u], P[i]); - ++u; - } - std::sort(P.begin() + u, P.end(), lex_greater()); - std::rotate(P.begin(), P.begin() + 1, P.end()); - // lower hull - size_t l = u; - size_t k = u - 1; - for (size_t i = l; i < n; ++i) - { - while (l > k && !is_a_right_turn(P[l-2], P[l-1], P[i])) - { - --l; - } - std::swap(P[l], P[i]); - ++l; - } - P.resize(l); -} - - -//////////////////////////////////////////////////////////////////////////////// // numerical routines /* * Compute the binomial coefficient (n, k) */ -inline double binomial(unsigned int n, unsigned int k) { return choose<double>(n, k); @@ -212,7 +127,6 @@ double binomial(unsigned int n, unsigned int k) /* * Compute the determinant of the 2x2 matrix with column the point P1, P2 */ -inline double det(Point const& P1, Point const& P2) { return P1[X]*P2[Y] - P1[Y]*P2[X]; @@ -222,7 +136,6 @@ double det(Point const& P1, Point const& P2) * Solve the linear system [P1,P2] * P = Q * in case there isn't exactly one solution the routine returns false */ -inline bool solve(Point & P, Point const& P1, Point const& P2, Point const& Q) { double d = det(P1, P2); @@ -239,27 +152,11 @@ bool solve(Point & P, Point const& P1, Point const& P2, Point const& Q) /* * Map the sub-interval I in [0,1] into the interval J and assign it to J */ -inline void map_to(Interval & J, Interval const& I) { - double length = J.extent(); - J[1] = I.max() * length + J[0]; - J[0] = I.min() * length + J[0]; + J.setEnds(J.valueAt(I.min()), J.valueAt(I.max())); } -/* - * The interval [1,0] is used to represent the empty interval, this routine - * is just an helper function for creating such an interval - */ -inline -Interval make_empty_interval() -{ - Interval I(0); - I[0] = 1; - return I; -} - - //////////////////////////////////////////////////////////////////////////////// // bezier curve routines @@ -267,8 +164,8 @@ Interval make_empty_interval() * Return true if all the Bezier curve control points are near, * false otherwise */ -inline -bool is_constant(std::vector<Point> const& A, double precision = EPSILON) +// Bezier.isConstant(precision) +bool is_constant(std::vector<Point> const& A, double precision) { for (unsigned int i = 1; i < A.size(); ++i) { @@ -281,7 +178,7 @@ bool is_constant(std::vector<Point> const& A, double precision = EPSILON) /* * Compute the hodograph of the bezier curve B and return it in D */ -inline +// derivative(Bezier) void derivative(std::vector<Point> & D, std::vector<Point> const& B) { D.clear(); @@ -304,7 +201,7 @@ void derivative(std::vector<Point> & D, std::vector<Point> const& B) * Compute the hodograph of the Bezier curve B rotated of 90 degree * and return it in D; we have N(t) orthogonal to B(t) for any t */ -inline +// rot90(derivative(Bezier)) void normal(std::vector<Point> & N, std::vector<Point> const& B) { derivative(N,B); @@ -317,7 +214,7 @@ void normal(std::vector<Point> & N, std::vector<Point> const& B) /* * Compute the portion of the Bezier curve "B" wrt the interval [0,t] */ -inline +// portion(Bezier, 0, t) void left_portion(Coord t, std::vector<Point> & B) { size_t n = B.size(); @@ -333,7 +230,7 @@ void left_portion(Coord t, std::vector<Point> & B) /* * Compute the portion of the Bezier curve "B" wrt the interval [t,1] */ -inline +// portion(Bezier, t, 1) void right_portion(Coord t, std::vector<Point> & B) { size_t n = B.size(); @@ -349,7 +246,7 @@ void right_portion(Coord t, std::vector<Point> & B) /* * Compute the portion of the Bezier curve "B" wrt the interval "I" */ -inline +// portion(Bezier, I) void portion (std::vector<Point> & B , Interval const& I) { if (I.min() == 0) @@ -371,9 +268,9 @@ void portion (std::vector<Point> & B , Interval const& I) struct intersection_point_tag; struct collinear_normal_tag; template <typename Tag> -void clip(Interval & dom, - std::vector<Point> const& A, - std::vector<Point> const& B); +OptInterval clip(std::vector<Point> const& A, + std::vector<Point> const& B, + double precision); template <typename Tag> void iterate(std::vector<Interval>& domsA, std::vector<Interval>& domsB, @@ -392,14 +289,14 @@ void iterate(std::vector<Interval>& domsA, * the line is returned in the output parameter "l" in the form of a 3 element * vector : l[0] * x + l[1] * y + l[2] == 0; the line is normalized. */ -inline +// Line(c[i], c[j]) void orientation_line (std::vector<double> & l, std::vector<Point> const& c, size_t i, size_t j) { l[0] = c[j][Y] - c[i][Y]; l[1] = c[i][X] - c[j][X]; - l[2] = cross(c[i], c[j]); + l[2] = cross(c[j], c[i]); double length = std::sqrt(l[0] * l[0] + l[1] * l[1]); assert (length != 0); l[0] /= length; @@ -411,22 +308,20 @@ void orientation_line (std::vector<double> & l, * Pick up an orientation line for the Bezier curve "c" and return it in * the output parameter "l" */ -inline -void pick_orientation_line (std::vector<double> & l, - std::vector<Point> const& c) +Line pick_orientation_line (std::vector<Point> const &c, double precision) { size_t i = c.size(); - while (--i > 0 && are_near(c[0], c[i])) + while (--i > 0 && are_near(c[0], c[i], precision)) {} - if (i == 0) - { - // this should never happen because when a new curve portion is created - // we check that it is not constant; - // however this requires that the precision used in the is_constant - // routine has to be the same used here in the are_near test - assert(i != 0); - } - orientation_line(l, c, 0, i); + + // this should never happen because when a new curve portion is created + // we check that it is not constant; + // however this requires that the precision used in the is_constant + // routine has to be the same used here in the are_near test + assert(i != 0); + + Line line(c[0], c[i]); + return line; //std::cerr << "i = " << i << std::endl; } @@ -436,29 +331,25 @@ void pick_orientation_line (std::vector<double> & l, * the line is returned in the output parameter "l" in the form of a 3 element * vector : l[0] * x + l[1] * y + l[2] == 0; the line is normalized. */ -inline -void orthogonal_orientation_line (std::vector<double> & l, - std::vector<Point> const& c, - Point const& p) +Line orthogonal_orientation_line (std::vector<Point> const &c, + Point const &p, + double precision) { - if (is_constant(c)) - { - // this should never happen - assert(!is_constant(c)); - } - std::vector<Point> ol(2); - ol[0] = p; - ol[1] = (c.back() - c.front()).cw() + p; - orientation_line(l, ol, 0, 1); + // this should never happen + assert(!is_constant(c, precision)); + + Line line(p, (c.back() - c.front()).cw() + p); + return line; } /* * Compute the signed distance of the point "P" from the normalized line l */ -inline -double distance (Point const& P, std::vector<double> const& l) +double signed_distance(Point const &p, Line const &l) { - return l[X] * P[X] + l[Y] * P[Y] + l[2]; + Coord a, b, c; + l.coefficients(a, b, c); + return a * p[X] + b * p[Y] + c; } /* @@ -466,26 +357,20 @@ double distance (Point const& P, std::vector<double> const& l) * curve "c" from the normalized orientation line "l". * This bounds are returned through the output Interval parameter"bound". */ -inline -void fat_line_bounds (Interval& bound, - std::vector<Point> const& c, - std::vector<double> const& l) +Interval fat_line_bounds (std::vector<Point> const &c, + Line const &l) { - bound[0] = 0; - bound[1] = 0; - for (size_t i = 0; i < c.size(); ++i) - { - const double d = distance(c[i], l); - if (bound[0] > d) bound[0] = d; - if (bound[1] < d) bound[1] = d; + Interval bound(0, 0); + for (size_t i = 0; i < c.size(); ++i) { + bound.expandTo(signed_distance(c[i], l)); } + return bound; } /* * return the x component of the intersection point between the line * passing through points p1, p2 and the line Y = "y" */ -inline double intersect (Point const& p1, Point const& p2, double y) { // we are sure that p2[Y] != p1[Y] because this routine is called @@ -500,23 +385,22 @@ double intersect (Point const& p1, Point const& p2, double y) * line "l" and the interval range "bound", the new parameter interval for * the clipped curve is returned through the output parameter "dom" */ -void clip_interval (Interval& dom, - std::vector<Point> const& B, - std::vector<double> const& l, - Interval const& bound) +OptInterval clip_interval (std::vector<Point> const& B, + Line const &l, + Interval const &bound) { double n = B.size() - 1; // number of sub-intervals std::vector<Point> D; // distance curve control points D.reserve (B.size()); for (size_t i = 0; i < B.size(); ++i) { - const double d = distance (B[i], l); + const double d = signed_distance(B[i], l); D.push_back (Point(i/n, d)); } //print(D); - convex_hull(D); - std::vector<Point> & p = D; + ConvexHull p; + p.swap(D); //print(p); bool plower, phigher; @@ -589,8 +473,11 @@ void clip_interval (Interval& dom, // << " : tmin = " << tmin << ", tmax = " << tmax << std::endl; } - dom[0] = tmin; - dom[1] = tmax; + if (tmin == 1 && tmax == 0) { + return OptInterval(); + } else { + return Interval(tmin, tmax); + } } /* @@ -599,24 +486,20 @@ void clip_interval (Interval& dom, * is returned through the output parameter "dom" */ template <> -inline -void clip<intersection_point_tag> (Interval & dom, - std::vector<Point> const& A, - std::vector<Point> const& B) +OptInterval clip<intersection_point_tag> (std::vector<Point> const& A, + std::vector<Point> const& B, + double precision) { - std::vector<double> bl(3); - Interval bound; - if (is_constant(A)) - { + Line bl; + if (is_constant(A, precision)) { Point M = middle_point(A.front(), A.back()); - orthogonal_orientation_line(bl, B, M); + bl = orthogonal_orientation_line(B, M, precision); + } else { + bl = pick_orientation_line(A, precision); } - else - { - pick_orientation_line(bl, A); - } - fat_line_bounds(bound, A, bl); - clip_interval(dom, B, bl, bound); + bl.normalize(); + Interval bound = fat_line_bounds(A, bl); + return clip_interval(B, bl, bound); } @@ -627,7 +510,6 @@ void clip<intersection_point_tag> (Interval & dom, * Compute a closed focus for the Bezier curve B and return it in F * A focus is any curve through which all lines perpendicular to B(t) pass. */ -inline void make_focus (std::vector<Point> & F, std::vector<Point> const& B) { assert (B.size() > 2); @@ -743,9 +625,8 @@ void distance_control_points (std::vector<Point> & D, * Clip the Bezier curve "B" wrt the focus "F"; the new parameter interval for * the clipped curve is returned through the output parameter "dom" */ -void clip_interval (Interval& dom, - std::vector<Point> const& B, - std::vector<Point> const& F) +OptInterval clip_interval (std::vector<Point> const& B, + std::vector<Point> const& F) { std::vector<Point> D; // distance curve control points distance_control_points(D, B, F); @@ -753,8 +634,8 @@ void clip_interval (Interval& dom, // ConvexHull chD(D); // std::vector<Point>& p = chD.boundary; // convex hull vertices - convex_hull(D); - std::vector<Point> & p = D; + ConvexHull p; + p.swap(D); //print(p, "CH(D)"); bool plower, clower; @@ -803,8 +684,11 @@ void clip_interval (Interval& dom, // std::cerr << "0 : lower " << p[0] // << " : tmin = " << tmin << ", tmax = " << tmax << std::endl; } - dom[0] = tmin; - dom[1] = tmax; + if (tmin == 1 && tmax == 0) { + return OptInterval(); + } else { + return Interval(tmin, tmax); + } } /* @@ -813,14 +697,13 @@ void clip_interval (Interval& dom, * for the clipped curve is returned through the output parameter "dom" */ template <> -inline -void clip<collinear_normal_tag> (Interval & dom, - std::vector<Point> const& A, - std::vector<Point> const& B) +OptInterval clip<collinear_normal_tag> (std::vector<Point> const& A, + std::vector<Point> const& B, + double /*precision*/) { std::vector<Point> F; make_focus(F, A); - clip_interval(dom, B, F); + return clip_interval(B, F); } @@ -828,9 +711,9 @@ void clip<collinear_normal_tag> (Interval & dom, const double MAX_PRECISION = 1e-8; const double MIN_CLIPPED_SIZE_THRESHOLD = 0.8; const Interval UNIT_INTERVAL(0,1); -const Interval EMPTY_INTERVAL = make_empty_interval(); +const OptInterval EMPTY_INTERVAL; const Interval H1_INTERVAL(0, 0.5); -const Interval H2_INTERVAL(0.5 + MAX_PRECISION, 1.0); +const Interval H2_INTERVAL(nextafter(0.5, 1.0), 1.0); /* * iterate @@ -884,9 +767,9 @@ void iterate<intersection_point_tag> (std::vector<Interval>& domsA, Interval* dom1 = &dompA; Interval* dom2 = &dompB; - Interval dom; + OptInterval dom; - if ( is_constant(A) && is_constant(B) ){ + if ( is_constant(A, precision) && is_constant(B, precision) ){ Point M1 = middle_point(C1->front(), C1->back()); Point M2 = middle_point(C2->front(), C2->back()); if (are_near(M1,M2)){ @@ -903,10 +786,9 @@ void iterate<intersection_point_tag> (std::vector<Interval>& domsA, #if VERBOSE std::cerr << "iter: " << iter << std::endl; #endif - clip<intersection_point_tag>(dom, *C1, *C2); + dom = clip<intersection_point_tag>(*C1, *C2, precision); - // [1,0] is utilized to represent an empty interval - if (dom == EMPTY_INTERVAL) + if (dom.isEmpty()) { #if VERBOSE std::cerr << "dom: empty" << std::endl; @@ -917,15 +799,12 @@ void iterate<intersection_point_tag> (std::vector<Interval>& domsA, std::cerr << "dom : " << dom << std::endl; #endif // all other cases where dom[0] > dom[1] are invalid - if (dom.min() > dom.max()) - { - assert(dom.min() < dom.max()); - } + assert(dom->min() <= dom->max()); - map_to(*dom2, dom); + map_to(*dom2, *dom); - portion(*C2, dom); - if (is_constant(*C2) && is_constant(*C1)) + portion(*C2, *dom); + if (is_constant(*C2, precision) && is_constant(*C1, precision)) { Point M1 = middle_point(C1->front(), C1->back()); Point M2 = middle_point(C2->front(), C2->back()); @@ -945,10 +824,10 @@ void iterate<intersection_point_tag> (std::vector<Interval>& domsA, // if we have clipped less than 20% than we need to subdive the curve // with the largest domain into two sub-curves - if ( dom.extent() > MIN_CLIPPED_SIZE_THRESHOLD) + if (dom->extent() > MIN_CLIPPED_SIZE_THRESHOLD) { #if VERBOSE - std::cerr << "clipped less than 20% : " << dom.extent() << std::endl; + std::cerr << "clipped less than 20% : " << dom->extent() << std::endl; std::cerr << "angle(pA) : " << angle(pA) << std::endl; std::cerr << "angle(pB) : " << angle(pB) << std::endl; #endif @@ -983,8 +862,8 @@ void iterate<intersection_point_tag> (std::vector<Interval>& domsA, return; } - std::swap(C1, C2); - std::swap(dom1, dom2); + swap(C1, C2); + swap(dom1, dom2); #if VERBOSE std::cerr << "dom(pA) : " << dompA << std::endl; std::cerr << "dom(pB) : " << dompB << std::endl; @@ -1047,7 +926,7 @@ void iterate<collinear_normal_tag> (std::vector<Interval>& domsA, Interval* dom1 = &dompA; Interval* dom2 = &dompB; - Interval dom; + OptInterval dom; size_t iter = 0; while (++iter < 100 @@ -1056,11 +935,9 @@ void iterate<collinear_normal_tag> (std::vector<Interval>& domsA, #if VERBOSE std::cerr << "iter: " << iter << std::endl; #endif - clip<collinear_normal_tag>(dom, *C1, *C2); + dom = clip<collinear_normal_tag>(*C1, *C2, precision); - // [1,0] is utilized to represent an empty interval - if (dom == EMPTY_INTERVAL) - { + if (dom.isEmpty()) { #if VERBOSE std::cerr << "dom: empty" << std::endl; #endif @@ -1069,13 +946,9 @@ void iterate<collinear_normal_tag> (std::vector<Interval>& domsA, #if VERBOSE std::cerr << "dom : " << dom << std::endl; #endif - // all other cases where dom[0] > dom[1] are invalid - if (dom.min() > dom.max()) - { - assert(dom.min() < dom.max()); - } + assert(dom->min() <= dom->max()); - map_to(*dom2, dom); + map_to(*dom2, *dom); // it's better to stop before losing computational precision if (iter > 1 && (dom2->extent() <= MAX_PRECISION)) @@ -1086,8 +959,8 @@ void iterate<collinear_normal_tag> (std::vector<Interval>& domsA, break; } - portion(*C2, dom); - if (iter > 1 && is_constant(*C2)) + portion(*C2, *dom); + if (iter > 1 && is_constant(*C2, precision)) { #if VERBOSE std::cerr << "new curve portion pC1 is constant" << std::endl; @@ -1098,10 +971,10 @@ void iterate<collinear_normal_tag> (std::vector<Interval>& domsA, // if we have clipped less than 20% than we need to subdive the curve // with the largest domain into two sub-curves - if ( dom.extent() > MIN_CLIPPED_SIZE_THRESHOLD) + if ( dom->extent() > MIN_CLIPPED_SIZE_THRESHOLD) { #if VERBOSE - std::cerr << "clipped less than 20% : " << dom.extent() << std::endl; + std::cerr << "clipped less than 20% : " << dom->extent() << std::endl; std::cerr << "angle(pA) : " << angle(pA) << std::endl; std::cerr << "angle(pB) : " << angle(pB) << std::endl; #endif @@ -1115,7 +988,7 @@ void iterate<collinear_normal_tag> (std::vector<Interval>& domsA, } pC1 = pC2 = pA; portion(pC1, H1_INTERVAL); - if (false && is_constant(pC1)) + if (false && is_constant(pC1, precision)) { #if VERBOSE std::cerr << "new curve portion pC1 is constant" << std::endl; @@ -1123,7 +996,7 @@ void iterate<collinear_normal_tag> (std::vector<Interval>& domsA, break; } portion(pC2, H2_INTERVAL); - if (is_constant(pC2)) + if (is_constant(pC2, precision)) { #if VERBOSE std::cerr << "new curve portion pC2 is constant" << std::endl; @@ -1146,7 +1019,7 @@ void iterate<collinear_normal_tag> (std::vector<Interval>& domsA, } pC1 = pC2 = pB; portion(pC1, H1_INTERVAL); - if (is_constant(pC1)) + if (is_constant(pC1, precision)) { #if VERBOSE std::cerr << "new curve portion pC1 is constant" << std::endl; @@ -1154,7 +1027,7 @@ void iterate<collinear_normal_tag> (std::vector<Interval>& domsA, break; } portion(pC2, H2_INTERVAL); - if (is_constant(pC2)) + if (is_constant(pC2, precision)) { #if VERBOSE std::cerr << "new curve portion pC2 is constant" << std::endl; @@ -1172,8 +1045,8 @@ void iterate<collinear_normal_tag> (std::vector<Interval>& domsA, return; } - std::swap(C1, C2); - std::swap(dom1, dom2); + swap(C1, C2); + swap(dom1, dom2); #if VERBOSE std::cerr << "dom(pA) : " << dompA << std::endl; std::cerr << "dom(pB) : " << dompB << std::endl; diff --git a/src/2geom/bezier-curve.cpp b/src/2geom/bezier-curve.cpp index 6dfb0f0b3..17221264b 100644 --- a/src/2geom/bezier-curve.cpp +++ b/src/2geom/bezier-curve.cpp @@ -32,6 +32,9 @@ */ #include <2geom/bezier-curve.h> +#include <2geom/path-sink.h> +#include <2geom/basic-intersection.h> +#include <2geom/nearest-time.h> namespace Geom { @@ -97,7 +100,7 @@ namespace Geom * @class BezierCurveN * @brief Bezier curve with compile-time specified order. * - * @tparam degree unsigned value indicating the order of the bezier curve + * @tparam degree unsigned value indicating the order of the Bezier curve * * @relates BezierCurve * @ingroup Curves @@ -105,12 +108,10 @@ namespace Geom BezierCurve::BezierCurve(std::vector<Point> const &pts) + : inner(pts) { - inner = D2<Bezier>(Bezier::Order(pts.size() - 1), Bezier::Order(pts.size() - 1)); - for (unsigned d = 0; d < 2; ++d) { - for (unsigned i = 0; i < pts.size(); i++) { - inner[d][i] = pts[i][d]; - } + if (pts.size() < 2) { + THROW_RANGEERROR("Bezier curve must have at least 2 control points"); } } @@ -124,16 +125,95 @@ Coord BezierCurve::length(Coord tolerance) const return distance(initialPoint(), finalPoint()); case 2: { - std::vector<Point> pts = points(); + std::vector<Point> pts = controlPoints(); return bezier_length(pts[0], pts[1], pts[2], tolerance); } case 3: { - std::vector<Point> pts = points(); + std::vector<Point> pts = controlPoints(); return bezier_length(pts[0], pts[1], pts[2], pts[3], tolerance); } default: - return bezier_length(points(), tolerance); + return bezier_length(controlPoints(), tolerance); + } +} + +std::vector<CurveIntersection> +BezierCurve::intersect(Curve const &other, Coord eps) const +{ + std::vector<CurveIntersection> result; + + // in case we encounter an order-1 curve created from a vector + // or a degenerate elliptical arc + if (isLineSegment()) { + LineSegment ls(initialPoint(), finalPoint()); + result = ls.intersect(other); + return result; + } + + // here we are sure that this curve is at least a quadratic Bezier + BezierCurve const *bez = dynamic_cast<BezierCurve const *>(&other); + if (bez) { + std::vector<std::pair<double, double> > xs; + find_intersections(xs, inner, bez->inner, eps); + for (unsigned i = 0; i < xs.size(); ++i) { + CurveIntersection x(*this, other, xs[i].first, xs[i].second); + result.push_back(x); + } + return result; + } + + // pass other intersection types to the other curve + result = other.intersect(*this, eps); + transpose_in_place(result); + return result; +} + +bool BezierCurve::operator==(Curve const &c) const +{ + if (this == &c) return true; + + BezierCurve const *other = dynamic_cast<BezierCurve const *>(&c); + if (!other) return false; + if (size() != other->size()) return false; + + for (unsigned i = 0; i < size(); ++i) { + if (controlPoint(i) != other->controlPoint(i)) return false; + } + return true; +} + +Coord BezierCurve::nearestTime(Point const &p, Coord from, Coord to) const +{ + return nearest_time(p, inner, from, to); +} + +void BezierCurve::feed(PathSink &sink, bool moveto_initial) const +{ + if (size() > 4) { + Curve::feed(sink, moveto_initial); + return; + } + + Point ip = controlPoint(0); + if (moveto_initial) { + sink.moveTo(ip); + } + switch (size()) { + case 2: + sink.lineTo(controlPoint(1)); + break; + case 3: + sink.quadTo(controlPoint(1), controlPoint(2)); + break; + case 4: + sink.curveTo(controlPoint(1), controlPoint(2), controlPoint(3)); + break; + default: + // TODO: add a path sink method that accepts a vector of control points + // and converts to cubic spline by default + assert(false); + break; } } @@ -164,9 +244,11 @@ Curve *BezierCurveN<1>::derivative() const { } template<> -Coord BezierCurveN<1>::nearestPoint(Point const& p, Coord from, Coord to) const +Coord BezierCurveN<1>::nearestTime(Point const& p, Coord from, Coord to) const { - if ( from > to ) std::swap(from, to); + using std::swap; + + if ( from > to ) swap(from, to); Point ip = pointAt(from); Point fp = pointAt(to); Point v = fp - ip; @@ -178,6 +260,53 @@ Coord BezierCurveN<1>::nearestPoint(Point const& p, Coord from, Coord to) const else return from + t*(to-from); } +template <> +std::vector<CurveIntersection> BezierCurveN<1>::intersect(Curve const &other, Coord eps) const +{ + std::vector<CurveIntersection> result; + + // only handle intersections with other LineSegments here + if (other.isLineSegment()) { + Line this_line(initialPoint(), finalPoint()); + Line other_line(other.initialPoint(), other.finalPoint()); + result = this_line.intersect(other_line); + filter_line_segment_intersections(result, true, true); + return result; + } + + // pass all other types to the other curve + result = other.intersect(*this, eps); + transpose_in_place(result); + return result; +} + +template <> +void BezierCurveN<1>::feed(PathSink &sink, bool moveto_initial) const +{ + if (moveto_initial) { + sink.moveTo(controlPoint(0)); + } + sink.lineTo(controlPoint(1)); +} + +template <> +void BezierCurveN<2>::feed(PathSink &sink, bool moveto_initial) const +{ + if (moveto_initial) { + sink.moveTo(controlPoint(0)); + } + sink.quadTo(controlPoint(1), controlPoint(2)); +} + +template <> +void BezierCurveN<3>::feed(PathSink &sink, bool moveto_initial) const +{ + if (moveto_initial) { + sink.moveTo(controlPoint(0)); + } + sink.curveTo(controlPoint(1), controlPoint(2), controlPoint(3)); +} + static Coord bezier_length_internal(std::vector<Point> &v1, Coord tolerance) { @@ -186,7 +315,7 @@ static Coord bezier_length_internal(std::vector<Point> &v1, Coord tolerance) * but shorter than the length of the polyline formed by its control * points. When the difference between the two values is smaller than the * error tolerance, we can be sure that the true value is no further than - * 2*tolerance from their arithmetic mean. When it's larger, we recursively + * 0.5 * tolerance from their arithmetic mean. When it's larger, we recursively * subdivide the Bezier curve into two parts and add their lengths. */ Coord lower = distance(v1.front(), v1.back()); diff --git a/src/2geom/bezier-curve.h b/src/2geom/bezier-curve.h index d379526fa..9ac4d7b4d 100644 --- a/src/2geom/bezier-curve.h +++ b/src/2geom/bezier-curve.h @@ -33,8 +33,8 @@ * the specific language governing rights and limitations. */ -#ifndef SEEN_LIB2GEOM_BEZIER_CURVE_H -#define SEEN_LIB2GEOM_BEZIER_CURVE_H +#ifndef LIB2GEOM_SEEN_BEZIER_CURVE_H +#define LIB2GEOM_SEEN_BEZIER_CURVE_H #include <2geom/curve.h> #include <2geom/sbasis-curve.h> // for non-native winding method @@ -48,25 +48,35 @@ class BezierCurve : public Curve { protected: D2<Bezier> inner; BezierCurve() {} - BezierCurve(D2<Bezier> const &b) : inner(b) {} BezierCurve(Bezier const &x, Bezier const &y) : inner(x, y) {} BezierCurve(std::vector<Point> const &pts); public: + explicit BezierCurve(D2<Bezier> const &b) : inner(b) {} + /// @name Access and modify control points /// @{ /** @brief Get the order of the Bezier curve. * A Bezier curve has order() + 1 control points. */ unsigned order() const { return inner[X].order(); } + /** @brief Get the number of control points. */ + unsigned size() const { return inner[X].order() + 1; } + /** @brief Access control points of the curve. + * @param ix The (zero-based) index of the control point. Note that the caller is responsible for checking that this value is <= order(). + * @return The control point. No-reference return, use setPoint() to modify control points. */ + Point controlPoint(unsigned ix) const { return Point(inner[X][ix], inner[Y][ix]); } + Point operator[](unsigned ix) const { return Point(inner[X][ix], inner[Y][ix]); } /** @brief Get the control points. * @return Vector with order() + 1 control points. */ - std::vector<Point> points() const { return bezier_points(inner); } + std::vector<Point> controlPoints() const { return bezier_points(inner); } + D2<Bezier> const &fragment() const { return inner; } + /** @brief Modify a control point. * @param ix The zero-based index of the point to modify. Note that the caller is responsible for checking that this value is <= order(). * @param v The new value of the point */ - void setPoint(unsigned ix, Point v) { - inner[X].setPoint(ix, v[X]); - inner[Y].setPoint(ix, v[Y]); + void setPoint(unsigned ix, Point const &v) { + inner[X][ix] = v[X]; + inner[Y][ix] = v[Y]; } /** @brief Set new control points. * @param ps Vector which must contain order() + 1 points. @@ -80,23 +90,22 @@ public: setPoint(i, ps[i]); } } - /** @brief Access control points of the curve. - * @param ix The (zero-based) index of the control point. Note that the caller is responsible for checking that this value is <= order(). - * @return The control point. No-reference return, use setPoint() to modify control points. */ - Point const operator[](unsigned ix) const { return Point(inner[X][ix], inner[Y][ix]); } /// @} /// @name Construct a Bezier curve with runtime-determined order. /// @{ - /** @brief Construct a curve from a vector of control points. */ + /** @brief Construct a curve from a vector of control points. + * This will construct the appropriate specialization of BezierCurve (i.e. LineSegment, + * QuadraticBezier or Cubic Bezier) if the number of control points in the passed vector + * does not exceed 4. */ static BezierCurve *create(std::vector<Point> const &pts); /// @} // implementation of virtual methods goes here -#ifndef DOXYGEN_SHOULD_SKIP_THIS virtual Point initialPoint() const { return inner.at0(); } virtual Point finalPoint() const { return inner.at1(); } - virtual bool isDegenerate() const { return inner.isConstant(); } + virtual bool isDegenerate() const { return inner.isConstant(0); } + virtual bool isLineSegment() const { return size() == 2; } virtual void setInitial(Point const &v) { setPoint(0, v); } virtual void setFinal(Point const &v) { setPoint(order(), v); } virtual Rect boundsFast() const { return *bounds_fast(inner); } @@ -120,19 +129,24 @@ public: return new BezierCurve(Geom::reverse(inner)); } - virtual Curve *transformed(Affine const &m) const { - BezierCurve *ret = new BezierCurve(); - std::vector<Point> ps = points(); - for (unsigned i = 0; i <= order(); i++) { - ps[i] = ps[i] * m; + using Curve::operator*=; + virtual void operator*=(Translate const &tr) { + for (unsigned i = 0; i < size(); ++i) { + inner[X][i] += tr[X]; + inner[Y][i] += tr[Y]; + } + } + virtual void operator*=(Scale const &s) { + for (unsigned i = 0; i < size(); ++i) { + inner[X][i] *= s[X]; + inner[Y][i] *= s[Y]; + } + } + virtual void operator*=(Affine const &m) { + for (unsigned i = 0; i < size(); ++i) { + setPoint(i, controlPoint(i) * m); } - ret->setPoints(ps); - return ret; } - virtual Curve &operator*=(Translate const &m) { - inner += m.vector(); - return *this; - }; virtual Curve *derivative() const { return new BezierCurve(Geom::derivative(inner[X]), Geom::derivative(inner[Y])); @@ -143,26 +157,32 @@ public: virtual std::vector<Coord> roots(Coord v, Dim2 d) const { return (inner[d] - v).roots(); } + virtual Coord nearestTime(Point const &p, Coord from = 0, Coord to = 1) const; virtual Coord length(Coord tolerance) const; - virtual Point pointAt(Coord t) const { return inner.valueAt(t); } - virtual std::vector<Point> pointAndDerivatives(Coord t, unsigned n) const { return inner.valueAndDerivatives(t, n); } + virtual std::vector<CurveIntersection> intersect(Curve const &other, Coord eps = EPSILON) const; + virtual Point pointAt(Coord t) const { return inner.pointAt(t); } + virtual std::vector<Point> pointAndDerivatives(Coord t, unsigned n) const { + return inner.valueAndDerivatives(t, n); + } virtual Coord valueAt(Coord t, Dim2 d) const { return inner[d].valueAt(t); } virtual D2<SBasis> toSBasis() const {return inner.toSBasis(); } -#endif + virtual bool operator==(Curve const &c) const; + virtual void feed(PathSink &sink, bool) const; }; template <unsigned degree> -class BezierCurveN : public BezierCurve { +class BezierCurveN + : public BezierCurve +{ + template <unsigned required_degree> + static void assert_degree(BezierCurveN<required_degree> const *) {} public: - template <unsigned required_degree> - static void assert_degree(BezierCurveN<required_degree> const *) {} - /// @name Construct Bezier curves /// @{ /** @brief Construct a Bezier curve of the specified order with all points zero. */ BezierCurveN() { - inner = D2<Bezier> (Bezier::Order(degree), Bezier::Order(degree)); + inner = D2<Bezier>(Bezier(Bezier::Order(degree)), Bezier(Bezier::Order(degree))); } /** @brief Construct from 2D Bezier polynomial. */ @@ -224,7 +244,10 @@ public: BezierCurveN(sx.second, sy.second)); } -#ifndef DOXYGEN_SHOULD_SKIP_THIS + virtual bool isLineSegment() const { + return size() == 2; + } + virtual Curve *duplicate() const { return new BezierCurveN(*this); } @@ -242,30 +265,19 @@ public: return new BezierCurveN(Geom::reverse(inner)); } } - virtual Curve *transformed(Affine const &m) const { - if (degree == 1) { - return new BezierCurveN<1>(initialPoint() * m, finalPoint() * m); - } else { - BezierCurveN *ret = new BezierCurveN(); - std::vector<Point> ps = points(); - for (unsigned i = 0; i <= degree; i++) { - ps[i] = ps[i] * m; - } - ret->setPoints(ps); - return ret; - } + virtual Curve *derivative() const; + + virtual Coord nearestTime(Point const &p, Coord from = 0, Coord to = 1) const { + return BezierCurve::nearestTime(p, from, to); } - virtual Curve &operator*=(Translate const &m) { - inner += m.vector(); - return *this; + virtual std::vector<CurveIntersection> intersect(Curve const &other, Coord eps = EPSILON) const { + // call super. this is implemented only to allow specializations + return BezierCurve::intersect(other, eps); } - virtual Curve *derivative() const; - - // the method below is defined so that LineSegment can specialize it - virtual Coord nearestPoint(Point const& p, Coord from = 0, Coord to = 1) const { - return Curve::nearestPoint(p, from, to); + virtual void feed(PathSink &sink, bool moveto_initial) const { + // call super. this is implemented only to allow specializations + BezierCurve::feed(sink, moveto_initial); } -#endif }; // BezierCurveN<0> is meaningless; specialize it out @@ -291,9 +303,14 @@ Curve *BezierCurveN<degree>::derivative() const { return new BezierCurveN<degree-1>(Geom::derivative(inner[X]), Geom::derivative(inner[Y])); } -// optimized specializations for LineSegment +// optimized specializations +template <> inline bool BezierCurveN<1>::isLineSegment() const { return true; } template <> Curve *BezierCurveN<1>::derivative() const; -template <> Coord BezierCurveN<1>::nearestPoint(Point const &, Coord, Coord) const; +template <> Coord BezierCurveN<1>::nearestTime(Point const &, Coord, Coord) const; +template <> std::vector<CurveIntersection> BezierCurveN<1>::intersect(Curve const &, Coord) const; +template <> void BezierCurveN<1>::feed(PathSink &sink, bool moveto_initial) const; +template <> void BezierCurveN<2>::feed(PathSink &sink, bool moveto_initial) const; +template <> void BezierCurveN<3>::feed(PathSink &sink, bool moveto_initial) const; inline Point middle_point(LineSegment const& _segment) { return ( _segment.initialPoint() + _segment.finalPoint() ) / 2; @@ -309,7 +326,7 @@ Coord bezier_length(Point p0, Point p1, Point p2, Point p3, Coord tolerance = 0. } // end namespace Geom -#endif // _2GEOM_BEZIER_CURVE_H_ +#endif // LIB2GEOM_SEEN_BEZIER_CURVE_H /* Local Variables: diff --git a/src/2geom/bezier-to-sbasis.h b/src/2geom/bezier-to-sbasis.h index 8cd4bf444..73c55d9b2 100644 --- a/src/2geom/bezier-to-sbasis.h +++ b/src/2geom/bezier-to-sbasis.h @@ -29,8 +29,8 @@ * */ -#ifndef _BEZIER_TO_SBASIS -#define _BEZIER_TO_SBASIS +#ifndef LIB2GEOM_SEEN_BEZIER_TO_SBASIS_H +#define LIB2GEOM_SEEN_BEZIER_TO_SBASIS_H #include <2geom/coord.h> #include <2geom/point.h> @@ -79,13 +79,9 @@ D2<SBasis> handles_to_sbasis(T const& handles, unsigned order) return sbc; } - } // end namespace Geom - - - -#endif +#endif // LIB2GEOM_SEEN_BEZIER_TO_SBASIS_H /* Local Variables: mode:c++ diff --git a/src/2geom/bezier-utils.cpp b/src/2geom/bezier-utils.cpp index ec17f6869..816bcdeb4 100644 --- a/src/2geom/bezier-utils.cpp +++ b/src/2geom/bezier-utils.cpp @@ -666,9 +666,9 @@ Point bezier_pt(unsigned const degree, Point const V[], double const t) { /** Pascal's triangle. */ - static int const pascal[4][4] = {{1}, - {1, 1}, - {1, 2, 1}, + static int const pascal[4][4] = {{1, 0, 0, 0}, + {1, 1, 0, 0}, + {1, 2, 1, 0}, {1, 3, 3, 1}}; assert( degree < 4); double const s = 1.0 - t; diff --git a/src/2geom/bezier.cpp b/src/2geom/bezier.cpp new file mode 100644 index 000000000..0c9d12c3b --- /dev/null +++ b/src/2geom/bezier.cpp @@ -0,0 +1,324 @@ +/** + * @file + * @brief Bernstein-Bezier polynomial + *//* + * Authors: + * MenTaLguY <mental@rydia.net> + * Michael Sloan <mgsloan@gmail.com> + * Nathan Hurst <njh@njhurst.com> + * Krzysztof Kosiński <tweenk.pl@gmail.com> + * + * Copyright 2007-2015 Authors + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + */ + +#include <2geom/bezier.h> +#include <2geom/solver.h> +#include <2geom/concepts.h> + +namespace Geom { + +std::vector<Coord> Bezier::valueAndDerivatives(Coord t, unsigned n_derivs) const { + /* This is inelegant, as it uses several extra stores. I think there might be a way to + * evaluate roughly in situ. */ + + // initialize return vector with zeroes, such that we only need to replace the non-zero derivs + std::vector<Coord> val_n_der(n_derivs + 1, Coord(0.0)); + + // initialize temp storage variables + std::valarray<Coord> d_(order()+1); + for(unsigned i = 0; i < size(); i++) { + d_[i] = c_[i]; + } + + unsigned nn = n_derivs + 1; + if(n_derivs > order()) { + nn = order()+1; // only calculate the non zero derivs + } + for(unsigned di = 0; di < nn; di++) { + //val_n_der[di] = (casteljau_subdivision(t, &d_[0], NULL, NULL, order() - di)); + val_n_der[di] = bernstein_value_at(t, &d_[0], order() - di); + for(unsigned i = 0; i < order() - di; i++) { + d_[i] = (order()-di)*(d_[i+1] - d_[i]); + } + } + + return val_n_der; +} + +void Bezier::subdivide(Coord t, Bezier *left, Bezier *right) const +{ + if (left) { + left->c_.resize(size()); + if (right) { + right->c_.resize(size()); + casteljau_subdivision<double>(t, &const_cast<std::valarray<Coord>&>(c_)[0], + &left->c_[0], &right->c_[0], order()); + } else { + casteljau_subdivision<double>(t, &const_cast<std::valarray<Coord>&>(c_)[0], + &left->c_[0], NULL, order()); + } + } else if (right) { + right->c_.resize(size()); + casteljau_subdivision<double>(t, &const_cast<std::valarray<Coord>&>(c_)[0], + NULL, &right->c_[0], order()); + } +} + +std::pair<Bezier, Bezier> Bezier::subdivide(Coord t) const +{ + std::pair<Bezier, Bezier> ret; + subdivide(t, &ret.first, &ret.second); + return ret; +} + +std::vector<Coord> Bezier::roots() const +{ + std::vector<Coord> solutions; + find_bezier_roots(solutions, 0, 1); + std::sort(solutions.begin(), solutions.end()); + return solutions; +} + +std::vector<Coord> Bezier::roots(Interval const &ivl) const +{ + std::vector<Coord> solutions; + find_bernstein_roots(&const_cast<std::valarray<Coord>&>(c_)[0], order(), solutions, 0, ivl.min(), ivl.max()); + std::sort(solutions.begin(), solutions.end()); + return solutions; +} + +Bezier Bezier::forward_difference(unsigned k) const +{ + Bezier fd(Order(order()-k)); + unsigned n = fd.size(); + + for(unsigned i = 0; i < n; i++) { + fd[i] = 0; + for(unsigned j = i; j < n; j++) { + fd[i] += (((j)&1)?-c_[j]:c_[j])*choose<double>(n, j-i); + } + } + return fd; +} + +Bezier Bezier::elevate_degree() const +{ + Bezier ed(Order(order()+1)); + unsigned n = size(); + ed[0] = c_[0]; + ed[n] = c_[n-1]; + for(unsigned i = 1; i < n; i++) { + ed[i] = (i*c_[i-1] + (n - i)*c_[i])/(n); + } + return ed; +} + +Bezier Bezier::reduce_degree() const +{ + if(order() == 0) return *this; + Bezier ed(Order(order()-1)); + unsigned n = size(); + ed[0] = c_[0]; + ed[n-1] = c_[n]; // ensure exact endpoints + unsigned middle = n/2; + for(unsigned i = 1; i < middle; i++) { + ed[i] = (n*c_[i] - i*ed[i-1])/(n-i); + } + for(unsigned i = n-1; i >= middle; i--) { + ed[i] = (n*c_[i] - i*ed[n-i])/(i); + } + return ed; +} + +Bezier Bezier::elevate_to_degree(unsigned newDegree) const +{ + Bezier ed = *this; + for(unsigned i = degree(); i < newDegree; i++) { + ed = ed.elevate_degree(); + } + return ed; +} + +Bezier Bezier::deflate() const +{ + if(order() == 0) return *this; + unsigned n = order(); + Bezier b(Order(n-1)); + for(unsigned i = 0; i < n; i++) { + b[i] = (n*c_[i+1])/(i+1); + } + return b; +} + +SBasis Bezier::toSBasis() const +{ + SBasis sb; + bezier_to_sbasis(sb, (*this)); + return sb; + //return bezier_to_sbasis(&c_[0], order()); +} + +Bezier &Bezier::operator+=(Bezier const &other) +{ + if (c_.size() > other.size()) { + c_ += other.elevate_to_degree(degree()).c_; + } else if (c_.size() < other.size()) { + *this = elevate_to_degree(other.degree()); + c_ += other.c_; + } else { + c_ += other.c_; + } + return *this; +} + +Bezier &Bezier::operator-=(Bezier const &other) +{ + if (c_.size() > other.size()) { + c_ -= other.elevate_to_degree(degree()).c_; + } else if (c_.size() < other.size()) { + *this = elevate_to_degree(other.degree()); + c_ -= other.c_; + } else { + c_ -= other.c_; + } + return *this; +} + + + +Bezier operator*(Bezier const &f, Bezier const &g) +{ + unsigned m = f.order(); + unsigned n = g.order(); + Bezier h(Bezier::Order(m+n)); + // h_k = sum_(i+j=k) (m i)f_i (n j)g_j / (m+n k) + + for(unsigned i = 0; i <= m; i++) { + const double fi = choose<double>(m,i)*f[i]; + for(unsigned j = 0; j <= n; j++) { + h[i+j] += fi * choose<double>(n,j)*g[j]; + } + } + for(unsigned k = 0; k <= m+n; k++) { + h[k] /= choose<double>(m+n, k); + } + return h; +} + +Bezier portion(Bezier const &a, double from, double to) +{ + Bezier ret(a); + + bool reverse_result = false; + if (from > to) { + std::swap(from, to); + reverse_result = true; + } + + do { + if (from == 0) { + if (to == 1) { + break; + } + casteljau_subdivision<double>(to, &ret.c_[0], &ret.c_[0], NULL, ret.order()); + break; + } + casteljau_subdivision<double>(from, &ret.c_[0], NULL, &ret.c_[0], ret.order()); + if (to == 1) break; + casteljau_subdivision<double>((to - from) / (1 - from), &ret.c_[0], &ret.c_[0], NULL, ret.order()); + // to protect against numerical inaccuracy in the above expression, we manually set + // the last coefficient to a value evaluated directly from the original polynomial + ret.c_[ret.order()] = a.valueAt(to); + } while(0); + + if (reverse_result) { + std::reverse(&ret.c_[0], &ret.c_[0] + ret.c_.size()); + } + return ret; +} + +Bezier derivative(Bezier const &a) +{ + //if(a.order() == 1) return Bezier(0.0); + if(a.order() == 1) return Bezier(a.c_[1]-a.c_[0]); + Bezier der(Bezier::Order(a.order()-1)); + + for(unsigned i = 0; i < a.order(); i++) { + der.c_[i] = a.order()*(a.c_[i+1] - a.c_[i]); + } + return der; +} + +Bezier integral(Bezier const &a) +{ + Bezier inte(Bezier::Order(a.order()+1)); + + inte[0] = 0; + for(unsigned i = 0; i < inte.order(); i++) { + inte[i+1] = inte[i] + a[i]/(inte.order()); + } + return inte; +} + +OptInterval bounds_fast(Bezier const &b) +{ + OptInterval ret = Interval::from_array(&const_cast<Bezier&>(b).c_[0], b.size()); + return ret; +} + +OptInterval bounds_exact(Bezier const &b) +{ + OptInterval ret(b.at0(), b.at1()); + std::vector<Coord> r = derivative(b).roots(); + for (unsigned i = 0; i < r.size(); ++i) { + ret->expandTo(b.valueAt(r[i])); + } + return ret; +} + +OptInterval bounds_local(Bezier const &b, OptInterval const &i) +{ + //return bounds_local(b.toSBasis(), i); + if (i) { + return bounds_fast(portion(b, i->min(), i->max())); + } else { + return OptInterval(); + } +} + +} // end namespace Geom + +/* + 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/2geom/bezier.h b/src/2geom/bezier.h index 51d5211d9..c41c2b3a7 100644 --- a/src/2geom/bezier.h +++ b/src/2geom/bezier.h @@ -1,13 +1,14 @@ /** * @file - * @brief Bezier polynomial + * @brief Bernstein-Bezier polynomial *//* * Authors: * MenTaLguY <mental@rydia.net> * Michael Sloan <mgsloan@gmail.com> * Nathan Hurst <njh@njhurst.com> + * Krzysztof Kosiński <tweenk.pl@gmail.com> * - * Copyright 2007 Authors + * Copyright 2007-2015 Authors * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public @@ -37,70 +38,23 @@ #ifndef LIB2GEOM_SEEN_BEZIER_H #define LIB2GEOM_SEEN_BEZIER_H +#include <algorithm> #include <valarray> #include <boost/optional.hpp> -#include <2geom/coord.h> #include <2geom/choose.h> -#include <valarray> -#include <2geom/math-utils.h> +#include <2geom/coord.h> #include <2geom/d2.h> -#include <2geom/solver.h> +#include <2geom/math-utils.h> namespace Geom { -inline Coord subdivideArr(Coord t, Coord const *v, Coord *left, Coord *right, unsigned order) { -/* - * Bernstein : - * Evaluate a Bernstein function at a particular parameter value - * Fill in control points for resulting sub-curves. - * - */ - - unsigned N = order+1; - std::valarray<Coord> row(N); - for (unsigned i = 0; i < N; i++) - row[i] = v[i]; - - // Triangle computation - const double omt = (1-t); - if(left) - left[0] = row[0]; - if(right) - right[order] = row[order]; - for (unsigned i = 1; i < N; i++) { - for (unsigned j = 0; j < N - i; j++) { - row[j] = omt*row[j] + t*row[j+1]; - } - if(left) - left[i] = row[0]; - if(right) - right[order-i] = row[order-i]; - } - return (row[0]); -/* - Coord vtemp[order+1][order+1]; - - // Copy control points - std::copy(v, v+order+1, vtemp[0]); - - // Triangle computation - for (unsigned i = 1; i <= order; i++) { - for (unsigned j = 0; j <= order - i; j++) { - vtemp[i][j] = lerp(t, vtemp[i-1][j], vtemp[i-1][j+1]); - } - } - if(left != NULL) - for (unsigned j = 0; j <= order; j++) - left[j] = vtemp[j][0]; - if(right != NULL) - for (unsigned j = 0; j <= order; j++) - right[j] = vtemp[order-j][j]; - - return (vtemp[order][0]);*/ -} - +/** @brief Compute the value of a Bernstein-Bezier polynomial. + * This method uses a Horner-like fast evaluation scheme. + * @param t Time value + * @param c_ Pointer to coefficients + * @param n Degree of the polynomial (number of coefficients minus one) */ template <typename T> -inline T bernsteinValueAt(double t, T const *c_, unsigned n) { +inline T bernstein_value_at(double t, T const *c_, unsigned n) { double u = 1.0 - t; double bc = 1; double tn = 1; @@ -113,16 +67,71 @@ inline T bernsteinValueAt(double t, T const *c_, unsigned n) { return (tmp + tn*t*c_[n]); } -class Bezier { +/** @brief Perform Casteljau subdivision of a Bezier polynomial. + * Given an array of coefficients and a time value, computes two new Bernstein-Bezier basis + * polynomials corresponding to the \f$[0, t]\f$ and \f$[t, 1]\f$ intervals of the original one. + * @param t Time value + * @param v Array of input coordinates + * @param left Output polynomial corresponding to \f$[0, t]\f$ + * @param right Output polynomial corresponding to \f$[t, 1]\f$ + * @param order Order of the input polynomial, equal to one less the number of coefficients + * @return Value of the polynomial at @a t */ +template <typename T> +inline T casteljau_subdivision(double t, T const *v, T *left, T *right, unsigned order) { + // The Horner-like scheme gives very slightly different results, but we need + // the result of subdivision to match exactly with Bezier's valueAt function. + T val = bernstein_value_at(t, v, order); + + if (!left && !right) { + return val; + } + + if (!right) { + if (left != v) { + std::copy(v, v + order + 1, left); + } + for (std::size_t i = order; i > 0; --i) { + for (std::size_t j = i; j <= order; ++j) { + left[j] = lerp(t, left[j-1], left[j]); + } + } + left[order] = val; + return left[order]; + } + + if (right != v) { + std::copy(v, v + order + 1, right); + } + for (std::size_t i = 1; i <= order; ++i) { + if (left) { + left[i-1] = right[0]; + } + for (std::size_t j = i; j > 0; --j) { + right[j-1] = lerp(t, right[j-1], right[j]); + } + } + right[0] = val; + if (left) { + left[order] = right[0]; + } + return right[0]; +} + +/** + * @brief Polynomial in Bernstein-Bezier basis + * @ingroup Fragments + */ +class Bezier + : boost::arithmetic< Bezier, double + , boost::additive< Bezier + > > +{ private: std::valarray<Coord> c_; friend Bezier portion(const Bezier & a, Coord from, Coord to); - friend OptInterval bounds_fast(Bezier const & b); - friend Bezier derivative(const Bezier & a); - friend class Bernstein; void @@ -130,13 +139,14 @@ private: double l, double r) const; protected: - Bezier(Coord const c[], unsigned ord) : c_(c, ord+1){ - //std::copy(c, c+order()+1, &c_[0]); - } + Bezier(Coord const c[], unsigned ord) + : c_(c, ord+1) + {} public: - unsigned int order() const { return c_.size()-1;} - unsigned int size() const { return c_.size();} + unsigned order() const { return c_.size()-1;} + unsigned degree() const { return order(); } + unsigned size() const { return c_.size();} Bezier() {} Bezier(const Bezier& b) :c_(b.c_) {} @@ -160,260 +170,147 @@ public: assert(ord.order == order()); } + /// @name Construct Bezier polynomials from their control points + /// @{ explicit Bezier(Coord c0) : c_(0., 1) { c_[0] = c0; } - - //Construct an order-1 bezier (linear Bézier) Bezier(Coord c0, Coord c1) : c_(0., 2) { c_[0] = c0; c_[1] = c1; } - - //Construct an order-2 bezier (quadratic Bézier) Bezier(Coord c0, Coord c1, Coord c2) : c_(0., 3) { c_[0] = c0; c_[1] = c1; c_[2] = c2; } - - //Construct an order-3 bezier (cubic Bézier) Bezier(Coord c0, Coord c1, Coord c2, Coord c3) : c_(0., 4) { c_[0] = c0; c_[1] = c1; c_[2] = c2; c_[3] = c3; } + Bezier(Coord c0, Coord c1, Coord c2, Coord c3, Coord c4) : c_(0., 5) { + c_[0] = c0; c_[1] = c1; c_[2] = c2; c_[3] = c3; c_[4] = c4; + } + Bezier(Coord c0, Coord c1, Coord c2, Coord c3, Coord c4, + Coord c5) : c_(0., 6) { + c_[0] = c0; c_[1] = c1; c_[2] = c2; c_[3] = c3; c_[4] = c4; + c_[5] = c5; + } + Bezier(Coord c0, Coord c1, Coord c2, Coord c3, Coord c4, + Coord c5, Coord c6) : c_(0., 7) { + c_[0] = c0; c_[1] = c1; c_[2] = c2; c_[3] = c3; c_[4] = c4; + c_[5] = c5; c_[6] = c6; + } + Bezier(Coord c0, Coord c1, Coord c2, Coord c3, Coord c4, + Coord c5, Coord c6, Coord c7) : c_(0., 8) { + c_[0] = c0; c_[1] = c1; c_[2] = c2; c_[3] = c3; c_[4] = c4; + c_[5] = c5; c_[6] = c6; c_[7] = c7; + } + Bezier(Coord c0, Coord c1, Coord c2, Coord c3, Coord c4, + Coord c5, Coord c6, Coord c7, Coord c8) : c_(0., 9) { + c_[0] = c0; c_[1] = c1; c_[2] = c2; c_[3] = c3; c_[4] = c4; + c_[5] = c5; c_[6] = c6; c_[7] = c7; c_[8] = c8; + } + Bezier(Coord c0, Coord c1, Coord c2, Coord c3, Coord c4, + Coord c5, Coord c6, Coord c7, Coord c8, Coord c9) : c_(0., 10) { + c_[0] = c0; c_[1] = c1; c_[2] = c2; c_[3] = c3; c_[4] = c4; + c_[5] = c5; c_[6] = c6; c_[7] = c7; c_[8] = c8; c_[9] = c9; + } - void resize (unsigned int n, Coord v = 0) - { - c_.resize (n, v); + template <typename Iter> + Bezier(Iter first, Iter last) { + c_.resize(std::distance(first, last)); + for (std::size_t i = 0; first != last; ++first, ++i) { + c_[i] = *first; + } } + Bezier(std::vector<Coord> const &vec) + : c_(&vec[0], vec.size()) + {} + /// @} - void clear() - { + void resize (unsigned int n, Coord v = 0) { + c_.resize (n, v); + } + void clear() { c_.resize(0); } - inline unsigned degree() const { return order(); } - //IMPL: FragmentConcept typedef Coord output_type; - inline bool isZero(double eps=EPSILON) const { + bool isZero(double eps=EPSILON) const { for(unsigned i = 0; i <= order(); i++) { if( ! are_near(c_[i], 0., eps) ) return false; } return true; } - inline bool isConstant(double eps=EPSILON) const { + bool isConstant(double eps=EPSILON) const { for(unsigned i = 1; i <= order(); i++) { if( ! are_near(c_[i], c_[0], eps) ) return false; } return true; } - inline bool isFinite() const { + bool isFinite() const { for(unsigned i = 0; i <= order(); i++) { if(!IS_FINITE(c_[i])) return false; } return true; } - inline Coord at0() const { return c_[0]; } - inline Coord at1() const { return c_[order()]; } - - inline Coord valueAt(double t) const { - int n = order(); - double u, bc, tn, tmp; - int i; - u = 1.0 - t; - bc = 1; - tn = 1; - tmp = c_[0]*u; - for(i=1; i<n; i++){ - tn = tn*t; - bc = bc*(n-i+1)/i; - tmp = (tmp + tn*bc*c_[i])*u; - } - return (tmp + tn*t*c_[n]); + Coord at0() const { return c_[0]; } + Coord &at0() { return c_[0]; } + Coord at1() const { return c_[order()]; } + Coord &at1() { return c_[order()]; } + + Coord valueAt(double t) const { + return bernstein_value_at(t, &c_[0], order()); } - inline Coord operator()(double t) const { return valueAt(t); } + Coord operator()(double t) const { return valueAt(t); } SBasis toSBasis() const; - //Only mutator - inline Coord &operator[](unsigned ix) { return c_[ix]; } - inline Coord const &operator[](unsigned ix) const { return const_cast<std::valarray<Coord>&>(c_)[ix]; } - //inline Coord const &operator[](unsigned ix) const { return c_[ix]; } - inline void setPoint(unsigned ix, double val) { c_[ix] = val; } - - /** - * The size of the returned vector equals n_derivs+1. - */ - std::vector<Coord> valueAndDerivatives(Coord t, unsigned n_derivs) const { - /* This is inelegant, as it uses several extra stores. I think there might be a way to - * evaluate roughly in situ. */ - - // initialize return vector with zeroes, such that we only need to replace the non-zero derivs - std::vector<Coord> val_n_der(n_derivs + 1, Coord(0.0)); - - // initialize temp storage variables - std::valarray<Coord> d_(order()+1); - for(unsigned i = 0; i < size(); i++) { - d_[i] = c_[i]; - } + Coord &operator[](unsigned ix) { return c_[ix]; } + Coord const &operator[](unsigned ix) const { return const_cast<std::valarray<Coord>&>(c_)[ix]; } - unsigned nn = n_derivs + 1; - if(n_derivs > order()) { - nn = order()+1; // only calculate the non zero derivs - } - for(unsigned di = 0; di < nn; di++) { - //val_n_der[di] = (subdivideArr(t, &d_[0], NULL, NULL, order() - di)); - val_n_der[di] = bernsteinValueAt(t, &d_[0], order() - di); - for(unsigned i = 0; i < order() - di; i++) { - d_[i] = (order()-di)*(d_[i+1] - d_[i]); - } - } + void setCoeff(unsigned ix, double val) { c_[ix] = val; } - return val_n_der; - } + // The size of the returned vector equals n_derivs+1. + std::vector<Coord> valueAndDerivatives(Coord t, unsigned n_derivs) const; - std::pair<Bezier, Bezier > subdivide(Coord t) const { - Bezier a(Bezier::Order(*this)), b(Bezier::Order(*this)); - subdivideArr(t, &const_cast<std::valarray<Coord>&>(c_)[0], &a.c_[0], &b.c_[0], order()); - return std::pair<Bezier, Bezier >(a, b); - } + void subdivide(Coord t, Bezier *left, Bezier *right) const; + std::pair<Bezier, Bezier> subdivide(Coord t) const; - std::vector<double> roots() const { - std::vector<double> solutions; - find_bezier_roots(solutions, 0, 1); - return solutions; - } - std::vector<double> roots(Interval const ivl) const { - std::vector<double> solutions; - find_bernstein_roots(&const_cast<std::valarray<Coord>&>(c_)[0], order(), solutions, 0, ivl.min(), ivl.max()); - return solutions; - } - - Bezier forward_difference(unsigned k) { - Bezier fd(Order(order()-k)); - unsigned n = fd.size(); - - for(unsigned i = 0; i < n; i++) { - fd[i] = 0; - for(unsigned j = i; j < n; j++) { - fd[i] += (((j)&1)?-c_[j]:c_[j])*choose<double>(n, j-i); - } - } - return fd; - } - - Bezier elevate_degree() const { - Bezier ed(Order(order()+1)); - unsigned n = size(); - ed[0] = c_[0]; - ed[n] = c_[n-1]; - for(unsigned i = 1; i < n; i++) { - ed[i] = (i*c_[i-1] + (n - i)*c_[i])/(n); - } - return ed; - } + std::vector<Coord> roots() const; + std::vector<Coord> roots(Interval const &ivl) const; - Bezier reduce_degree() const { - if(order() == 0) return *this; - Bezier ed(Order(order()-1)); - unsigned n = size(); - ed[0] = c_[0]; - ed[n-1] = c_[n]; // ensure exact endpoints - unsigned middle = n/2; - for(unsigned i = 1; i < middle; i++) { - ed[i] = (n*c_[i] - i*ed[i-1])/(n-i); - } - for(unsigned i = n-1; i >= middle; i--) { - ed[i] = (n*c_[i] - i*ed[n-i])/(i); - } - return ed; - } + Bezier forward_difference(unsigned k) const; + Bezier elevate_degree() const; + Bezier reduce_degree() const; + Bezier elevate_to_degree(unsigned newDegree) const; + Bezier deflate() const; - Bezier elevate_to_degree(unsigned newDegree) const { - Bezier ed = *this; - for(unsigned i = degree(); i < newDegree; i++) { - ed = ed.elevate_degree(); - } - return ed; + // basic arithmetic operators + Bezier &operator+=(double v) { + c_ += v; + return *this; } - - Bezier deflate() { - if(order() == 0) return *this; - unsigned n = order(); - Bezier b(Order(n-1)); - for(unsigned i = 0; i < n; i++) { - b[i] = (n*c_[i+1])/(i+1); - } - return b; + Bezier &operator-=(double v) { + c_ -= v; + return *this; } -}; - - -void bezier_to_sbasis (SBasis & sb, Bezier const& bz); - -inline -Bezier multiply(Bezier const& f, Bezier const& g) { - unsigned m = f.order(); - unsigned n = g.order(); - Bezier h(Bezier::Order(m+n)); - // h_k = sum_(i+j=k) (m i)f_i (n j)g_j / (m+n k) - - for(unsigned i = 0; i <= m; i++) { - const double fi = choose<double>(m,i)*f[i]; - for(unsigned j = 0; j <= n; j++) { - h[i+j] += fi * choose<double>(n,j)*g[j]; - } + Bezier &operator*=(double v) { + c_ *= v; + return *this; } - for(unsigned k = 0; k <= m+n; k++) { - h[k] /= choose<double>(m+n, k); + Bezier &operator/=(double v) { + c_ /= v; + return *this; } - return h; -} - -inline -SBasis Bezier::toSBasis() const { - SBasis sb; - bezier_to_sbasis(sb, (*this)); - return sb; - //return bezier_to_sbasis(&c_[0], order()); -} - -//TODO: implement others -inline Bezier operator+(const Bezier & a, double v) { - Bezier result = Bezier(Bezier::Order(a)); - for(unsigned i = 0; i <= a.order(); i++) - result[i] = a[i] + v; - return result; -} - -inline Bezier operator-(const Bezier & a, double v) { - Bezier result = Bezier(Bezier::Order(a)); - for(unsigned i = 0; i <= a.order(); i++) - result[i] = a[i] - v; - return result; -} + Bezier &operator+=(Bezier const &other); + Bezier &operator-=(Bezier const &other); +}; -inline Bezier& operator+=(Bezier & a, double v) { - for(unsigned i = 0; i <= a.order(); ++i) - a[i] = a[i] + v; - return a; -} -inline Bezier& operator-=(Bezier & a, double v) { - for(unsigned i = 0; i <= a.order(); ++i) - a[i] = a[i] - v; - return a; -} +void bezier_to_sbasis (SBasis &sb, Bezier const &bz); -inline Bezier operator*(const Bezier & a, double v) { - Bezier result = Bezier(Bezier::Order(a)); - for(unsigned i = 0; i <= a.order(); i++) - result[i] = a[i] * v; - return result; -} - -inline Bezier operator/(const Bezier & a, double v) { - Bezier result = Bezier(Bezier::Order(a)); - for(unsigned i = 0; i <= a.order(); i++) - result[i] = a[i] / v; +Bezier operator*(Bezier const &f, Bezier const &g); +inline Bezier multiply(Bezier const &f, Bezier const &g) { + Bezier result = f * g; return result; } @@ -424,20 +321,7 @@ inline Bezier reverse(const Bezier & a) { return result; } -inline Bezier portion(const Bezier & a, double from, double to) { - //TODO: implement better? - std::valarray<Coord> res(a.order() + 1); - if(from == 0) { - if(to == 1) { return Bezier(a); } - subdivideArr(to, &const_cast<Bezier&>(a).c_[0], &res[0], NULL, a.order()); - return Bezier(&res[0], a.order()); - } - subdivideArr(from, &const_cast<Bezier&>(a).c_[0], NULL, &res[0], a.order()); - if(to == 1) return Bezier(&res[0], a.order()); - std::valarray<Coord> res2(a.order()+1); - subdivideArr((to - from)/(1 - from), &res[0], &res2[0], NULL, a.order()); - return Bezier(&res2[0], a.order()); -} +Bezier portion(const Bezier & a, double from, double to); // XXX Todo: how to handle differing orders inline std::vector<Point> bezier_points(const D2<Bezier > & a) { @@ -450,52 +334,19 @@ inline std::vector<Point> bezier_points(const D2<Bezier > & a) { return result; } -inline Bezier derivative(const Bezier & a) { - //if(a.order() == 1) return Bezier(0.0); - if(a.order() == 1) return Bezier(a.c_[1]-a.c_[0]); - Bezier der(Bezier::Order(a.order()-1)); - - for(unsigned i = 0; i < a.order(); i++) { - der.c_[i] = a.order()*(a.c_[i+1] - a.c_[i]); - } - return der; -} - -inline Bezier integral(const Bezier & a) { - Bezier inte(Bezier::Order(a.order()+1)); - - inte[0] = 0; - for(unsigned i = 0; i < inte.order(); i++) { - inte[i+1] = inte[i] + a[i]/(inte.order()); - } - return inte; -} - -inline OptInterval bounds_fast(Bezier const & b) { - OptInterval ret = Interval::from_array(&const_cast<Bezier&>(b).c_[0], b.size()); - return ret; -} - -//TODO: better bounds exact -inline OptInterval bounds_exact(Bezier const & b) { - return bounds_exact(b.toSBasis()); -} - -inline OptInterval bounds_local(Bezier const & b, OptInterval i) { - //return bounds_local(b.toSBasis(), i); - if (i) { - return bounds_fast(portion(b, i->min(), i->max())); - } else { - return OptInterval(); - } -} +Bezier derivative(Bezier const &a); +Bezier integral(Bezier const &a); +OptInterval bounds_fast(Bezier const &b); +OptInterval bounds_exact(Bezier const &b); +OptInterval bounds_local(Bezier const &b, OptInterval const &i); -inline std::ostream &operator<< (std::ostream &out_file, const Bezier & b) { - out_file << "Bezier("; - for(unsigned i = 0; i < b.size(); i++) { - out_file << b[i] << ", "; +inline std::ostream &operator<< (std::ostream &os, const Bezier & b) { + os << "Bezier("; + for(unsigned i = 0; i < b.order(); i++) { + os << format_coord_nice(b[i]) << ", "; } - return out_file << ")"; + os << format_coord_nice(b[b.order()]) << ")"; + return os; } } diff --git a/src/2geom/cairo-path-sink.cpp b/src/2geom/cairo-path-sink.cpp new file mode 100644 index 000000000..244a08ba4 --- /dev/null +++ b/src/2geom/cairo-path-sink.cpp @@ -0,0 +1,123 @@ +/** + * @file + * @brief Path sink for Cairo contexts + *//* + * Copyright 2014 Krzysztof Kosiński + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, output to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + */ + +#include <cairo.h> +#include <2geom/cairo-path-sink.h> +#include <2geom/elliptical-arc.h> + +namespace Geom { + +CairoPathSink::CairoPathSink(cairo_t *cr) + : _cr(cr) +{} + +void CairoPathSink::moveTo(Point const &p) +{ + cairo_move_to(_cr, p[X], p[Y]); + _current_point = p; +} + +void CairoPathSink::lineTo(Point const &p) +{ + cairo_line_to(_cr, p[X], p[Y]); + _current_point = p; +} + +void CairoPathSink::curveTo(Point const &p1, Point const &p2, Point const &p3) +{ + cairo_curve_to(_cr, p1[X], p1[Y], p2[X], p2[Y], p3[X], p3[Y]); + _current_point = p3; +} + +void CairoPathSink::quadTo(Point const &p1, Point const &p2) +{ + // degree-elevate to cubic Bezier, since Cairo doesn't do quad Beziers + // google "Bezier degree elevation" for more info + Point q1 = (1./3.) * _current_point + (2./3.) * p1; + Point q2 = (2./3.) * p1 + (1./3.) * p2; + // q3 = p2 + cairo_curve_to(_cr, q1[X], q1[Y], q2[X], q2[Y], p2[X], p2[Y]); + _current_point = p2; +} + +void CairoPathSink::arcTo(double rx, double ry, double angle, + bool large_arc, bool sweep, Point const &p) +{ + EllipticalArc arc(_current_point, rx, ry, angle, large_arc, sweep, p); + // Cairo only does circular arcs. + // To do elliptical arcs, we must use a temporary transform. + Affine uct = arc.unitCircleTransform(); + + // TODO move Cairo-2Geom matrix conversion into a common location + cairo_matrix_t cm; + cm.xx = uct[0]; + cm.xy = uct[2]; + cm.x0 = uct[4]; + cm.yx = uct[1]; + cm.yy = uct[3]; + cm.y0 = uct[5]; + + cairo_save(_cr); + cairo_transform(_cr, &cm); + if (sweep) { + cairo_arc(_cr, 0, 0, 1, arc.initialAngle(), arc.finalAngle()); + } else { + cairo_arc_negative(_cr, 0, 0, 1, arc.initialAngle(), arc.finalAngle()); + } + _current_point = p; + cairo_restore(_cr); + + /* Note that an extra linear segment will be inserted before the arc + * if Cairo considers the current point distinct from the initial point + * of the arc; we could partially alleviate this by not emitting + * linear segments that are followed by arc segments, but this would require + * buffering the input curves. */ +} + +void CairoPathSink::closePath() +{ + cairo_close_path(_cr); +} + +void CairoPathSink::flush() {} + +} // namespace Geom + +/* + 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/2geom/conjugate_gradient.h b/src/2geom/cairo-path-sink.h index 4f500c0e6..9fec7e0ab 100644 --- a/src/2geom/conjugate_gradient.h +++ b/src/2geom/cairo-path-sink.h @@ -1,8 +1,8 @@ /** * @file - * @brief Routines for solving a system of linear equations using the conjugate gradient method + * @brief Path sink for Cairo contexts *//* - * Copyright 2006 Nathan Hurst <njh@mail.csse.monash.edu.au> + * Copyright 2014 Krzysztof Kosiński * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public @@ -29,29 +29,52 @@ * */ -#ifndef _2GEOM_CONJUGATE_GRADIENT_H -#define _2GEOM_CONJUGATE_GRADIENT_H +#ifndef LIB2GEOM_SEEN_CAIRO_PATH_SINK_H +#define LIB2GEOM_SEEN_CAIRO_PATH_SINK_H -#include <valarray> +#include <2geom/path-sink.h> +#include <cairo.h> + +namespace Geom { -namespace Geom -{ -double -inner(std::valarray<double> const &x, - std::valarray<double> const &y); +/** @brief Output paths to a Cairo drawing context + * + * This class converts from 2Geom path representation to the Cairo representation. + * Use it to simplify visualizing the results of 2Geom operations with the Cairo library, + * for example: + * @code + * CairoPathSink sink(cr); + * sink.feed(pv); + * cairo_stroke(cr); + * @endcode + * + * Currently the flush method is a no-op, but this is not guaranteed + * to hold forever. + */ +class CairoPathSink + : public PathSink +{ +public: + CairoPathSink(cairo_t *cr); -void -conjugate_gradient(std::valarray<double> const &A, - std::valarray<double> &x, - std::valarray<double> const &b, - unsigned n, double tol, - unsigned max_iterations, bool ortho1); + void moveTo(Point const &p); + void lineTo(Point const &p); + void curveTo(Point const &c0, Point const &c1, Point const &p); + void quadTo(Point const &c, Point const &p); + void arcTo(Coord rx, Coord ry, Coord angle, + bool large_arc, bool sweep, Point const &p); + void closePath(); + void flush(); -} // namespace Geom +private: + cairo_t *_cr; + Point _current_point; +}; -#endif // _2GEOM_CONJUGATE_GRADIENT_H +} +#endif // !LIB2GEOM_SEEN_CAIRO_PATH_SINK_H /* Local Variables: mode:c++ diff --git a/src/2geom/circle-circle.cpp b/src/2geom/circle-circle.cpp deleted file mode 100644 index 134fa33a2..000000000 --- a/src/2geom/circle-circle.cpp +++ /dev/null @@ -1,139 +0,0 @@ -/* circle_circle_intersection() * - * Determine the points where 2 circles in a common plane intersect. - * - * int circle_circle_intersection( - * // center and radius of 1st circle - * double x0, double y0, double r0, - * // center and radius of 2nd circle - * double x1, double y1, double r1, - * // 1st intersection point - * double *xi, double *yi, - * // 2nd intersection point - * double *xi_prime, double *yi_prime) - * - * This is a public domain work. 3/26/2005 Tim Voght - * Ported to lib2geom, 2006 Nathan Hurst - * - * Copyright 2006 Nathan Hurst <njh@mail.csse.monash.edu.au> - * - * This library is free software; you can redistribute it and/or - * modify it either under the terms of the GNU Lesser General Public - * License version 2.1 as published by the Free Software Foundation - * (the "LGPL") or, at your option, under the terms of the Mozilla - * Public License Version 1.1 (the "MPL"). If you do not alter this - * notice, a recipient may use your version of this file under either - * the MPL or the LGPL. - * - * You should have received a copy of the LGPL along with this library - * in the file COPYING-LGPL-2.1; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * You should have received a copy of the MPL along with this library - * in the file COPYING-MPL-1.1 - * - * The contents of this file are subject to the Mozilla Public License - * Version 1.1 (the "License"); you may not use this file except in - * compliance with the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY - * OF ANY KIND, either express or implied. See the LGPL or the MPL for - * the specific language governing rights and limitations. - * - */ -#include <stdio.h> -#include <math.h> -#include <2geom/point.h> - -namespace Geom{ - -int circle_circle_intersection(Point X0, double r0, - Point X1, double r1, - Point & p0, Point & p1) -{ - /* dx and dy are the vertical and horizontal distances between - * the circle centers. - */ - Point D = X1 - X0; - - /* Determine the straight-line distance between the centers. */ - double d = L2(D); - - /* Check for solvability. */ - if (d > (r0 + r1)) - { - /* no solution. circles do not intersect. */ - return 0; - } - if (d <= fabs(r0 - r1)) - { - /* no solution. one circle is contained in the other */ - return 1; - } - - /* 'point 2' is the point where the line through the circle - * intersection points crosses the line between the circle - * centers. - */ - - /* Determine the distance from point 0 to point 2. */ - double a = ((r0*r0) - (r1*r1) + (d*d)) / (2.0 * d) ; - - /* Determine the coordinates of point 2. */ - Point p2 = X0 + D * (a/d); - - /* Determine the distance from point 2 to either of the - * intersection points. - */ - double h = sqrt((r0*r0) - (a*a)); - - /* Now determine the offsets of the intersection points from - * point 2. - */ - Point r = (h/d)*rot90(D); - - /* Determine the absolute intersection points. */ - p0 = p2 + r; - p1 = p2 - r; - - return 2; -} - -}; - - -#ifdef TEST - -void run_test(double x0, double y0, double r0, - double x1, double y1, double r1) -{ - printf("x0=%F, y0=%F, r0=%F, x1=%F, y1=%F, r1=%F :\n", - x0, y0, r0, x1, y1, r1); - Geom::Point p0, p1; - Geom::circle_circle_intersection(Geom::Point(x0, y0), r0, - Geom::Point(x1, y1), r1, - p0, p1); - printf(" x3=%F, y3=%F, x3_prime=%F, y3_prime=%F\n", - p0[0], p0[1], p1[0], p1[1]); -} - -int main(void) -{ - /* Add more! */ - run_test(-1.0, -1.0, 1.5, 1.0, 1.0, 2.0); - run_test(1.0, -1.0, 1.5, -1.0, 1.0, 2.0); - run_test(-1.0, 1.0, 1.5, 1.0, -1.0, 2.0); - run_test(1.0, 1.0, 1.5, -1.0, -1.0, 2.0); - exit(0); -} -#endif - -/* - 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/2geom/circle.cpp b/src/2geom/circle.cpp index d021882ea..553981a72 100644 --- a/src/2geom/circle.cpp +++ b/src/2geom/circle.cpp @@ -1,10 +1,11 @@ -/* - * Circle Curve - * +/** @file + * @brief Circle shape + *//* * Authors: - * Marco Cecchetti <mrcekets at gmail.com> + * Marco Cecchetti <mrcekets at gmail.com> + * Krzysztof Kosiński <tweenk.pl@gmail.com> * - * Copyright 2008 authors + * Copyright 2008-2014 Authors * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public @@ -30,77 +31,241 @@ * the specific language governing rights and limitations. */ - #include <2geom/circle.h> #include <2geom/ellipse.h> -#include <2geom/svg-elliptical-arc.h> +#include <2geom/elliptical-arc.h> #include <2geom/numeric/fitting-tool.h> #include <2geom/numeric/fitting-model.h> +namespace Geom { - -namespace Geom +Rect Circle::boundsFast() const { + Point rr(_radius, _radius); + Rect bbox(_center - rr, _center + rr); + return bbox; +} -void Circle::set(double A, double B, double C, double D) +void Circle::setCoefficients(Coord A, Coord B, Coord C, Coord D) { - if (A == 0) - { + if (A == 0) { THROW_RANGEERROR("square term coefficient == 0"); } //std::cerr << "B = " << B << " C = " << C << " D = " << D << std::endl; - double b = B / A; - double c = C / A; - double d = D / A; + Coord b = B / A; + Coord c = C / A; + Coord d = D / A; - m_centre[X] = -b/2; - m_centre[Y] = -c/2; - double r2 = m_centre[X] * m_centre[X] + m_centre[Y] * m_centre[Y] - d; + _center[X] = -b/2; + _center[Y] = -c/2; + Coord r2 = _center[X] * _center[X] + _center[Y] * _center[Y] - d; - if (r2 < 0) - { + if (r2 < 0) { THROW_RANGEERROR("ray^2 < 0"); } - m_ray = std::sqrt(r2); + _radius = std::sqrt(r2); } +void Circle::coefficients(Coord &A, Coord &B, Coord &C, Coord &D) const +{ + A = 1; + B = -2 * _center[X]; + C = -2 * _center[Y]; + D = _center[X] * _center[X] + _center[Y] * _center[Y] - _radius * _radius; +} -void Circle::set(std::vector<Point> const& points) +std::vector<Coord> Circle::coefficients() const { - size_t sz = points.size(); - if (sz < 3) - { - THROW_RANGEERROR("fitting error: too few points passed"); + std::vector<Coord> c(4); + coefficients(c[0], c[1], c[2], c[3]); + return c; +} + + +Zoom Circle::unitCircleTransform() const +{ + Zoom ret(_radius, _center / _radius); + return ret; +} + +Zoom Circle::inverseUnitCircleTransform() const +{ + if (_radius == 0) { + THROW_RANGEERROR("degenerate circle does not have an inverse unit circle transform"); } - NL::LFMCircle model; - NL::least_squeares_fitter<NL::LFMCircle> fitter(model, sz); - for (size_t i = 0; i < sz; ++i) - { - fitter.append(points[i]); + Zoom ret(1/_radius, Translate(-_center)); + return ret; +} + +Point Circle::initialPoint() const +{ + Point p(_center); + p[X] += _radius; + return p; +} + +Point Circle::pointAt(Coord t) const { + return _center + Point::polar(t) * _radius; +} + +Coord Circle::valueAt(Coord t, Dim2 d) const { + Coord delta = (d == X ? std::cos(t) : std::sin(t)); + return _center[d] + delta * _radius; +} + +Coord Circle::timeAt(Point const &p) const { + if (_center == p) return 0; + return atan2(p - _center); +} + +Coord Circle::nearestTime(Point const &p) const { + return timeAt(p); +} + +bool Circle::contains(Rect const &r) const +{ + for (unsigned i = 0; i < 4; ++i) { + if (!contains(r.corner(i))) return false; } - fitter.update(); + return true; +} - NL::Vector z(sz, 0.0); - model.instance(*this, fitter.result(z)); +bool Circle::contains(Circle const &other) const +{ + Coord cdist = distance(_center, other._center); + Coord rdist = fabs(_radius - other._radius); + return cdist <= rdist; +} + +bool Circle::intersects(Line const &l) const +{ + // http://mathworld.wolfram.com/Circle-LineIntersection.html + Coord dr = l.versor().length(); + Coord r = _radius; + Coord D = cross(l.initialPoint(), l.finalPoint()); + Coord delta = r*r * dr*dr - D*D; + if (delta >= 0) return true; + return false; +} + +bool Circle::intersects(Circle const &other) const +{ + Coord cdist = distance(_center, other._center); + Coord rsum = _radius + other._radius; + return cdist <= rsum; +} + + +std::vector<ShapeIntersection> Circle::intersect(Line const &l) const +{ + // http://mathworld.wolfram.com/Circle-LineIntersection.html + Coord dr = l.versor().length(); + Coord dx = l.versor().x(); + Coord dy = l.versor().y(); + Coord D = cross(l.initialPoint() - _center, l.finalPoint() - _center); + Coord delta = _radius*_radius * dr*dr - D*D; + + std::vector<ShapeIntersection> result; + if (delta < 0) return result; + if (delta == 0) { + Coord ix = (D*dy) / (dr*dr); + Coord iy = (-D*dx) / (dr*dr); + Point ip(ix, iy); ip += _center; + result.push_back(ShapeIntersection(timeAt(ip), l.timeAt(ip), ip)); + return result; + } + + Coord sqrt_delta = std::sqrt(delta); + Coord signmod = dy < 0 ? -1 : 1; + + Coord i1x = (D*dy + signmod * dx * sqrt_delta) / (dr*dr); + Coord i1y = (-D*dx + fabs(dy) * sqrt_delta) / (dr*dr); + Point i1p(i1x, i1y); i1p += _center; + + Coord i2x = (D*dy - signmod * dx * sqrt_delta) / (dr*dr); + Coord i2y = (-D*dx - fabs(dy) * sqrt_delta) / (dr*dr); + Point i2p(i2x, i2y); i2p += _center; + + result.push_back(ShapeIntersection(timeAt(i1p), l.timeAt(i1p), i1p)); + result.push_back(ShapeIntersection(timeAt(i2p), l.timeAt(i2p), i2p)); + return result; +} + +std::vector<ShapeIntersection> Circle::intersect(LineSegment const &l) const +{ + std::vector<ShapeIntersection> result = intersect(Line(l)); + filter_line_segment_intersections(result); + return result; +} + +std::vector<ShapeIntersection> Circle::intersect(Circle const &other) const +{ + std::vector<ShapeIntersection> result; + + if (*this == other) { + THROW_INFINITESOLUTIONS(); + } + if (contains(other)) return result; + if (!intersects(other)) return result; + + // See e.g. http://mathworld.wolfram.com/Circle-CircleIntersection.html + // Basically, we figure out where is the third point of a triangle + // with two points in the centers and with edge lengths equal to radii + Point cv = other._center - _center; + Coord d = cv.length(); + Coord R = radius(), r = other.radius(); + + if (d == R + r) { + Point px = lerp(R / d, _center, other._center); + Coord T = timeAt(px), t = other.timeAt(px); + result.push_back(ShapeIntersection(T, t, px)); + return result; + } + + // q is the distance along the line between centers to the perpendicular line + // that goes through both intersections. + Coord q = (d*d - r*r + R*R) / (2*d); + Point qp = lerp(q/d, _center, other._center); + + // The triangle given by the points: + // _center, qp, intersection + // is a right triangle. Determine the distance between qp and intersection + // using the Pythagorean theorem. + Coord h = std::sqrt(R*R - q*q); + Point qd = (h/d) * cv.cw(); + + // now compute the intersection points + Point x1 = qp + qd; + Point x2 = qp - qd; + + result.push_back(ShapeIntersection(timeAt(x1), other.timeAt(x1), x1)); + result.push_back(ShapeIntersection(timeAt(x2), other.timeAt(x2), x2)); + return result; } /** @param inner a point whose angle with the circle center is inside the angle that the arc spans */ EllipticalArc * -Circle::arc(Point const& initial, Point const& inner, Point const& final, - bool _svg_compliant) +Circle::arc(Point const& initial, Point const& inner, Point const& final) const { // TODO native implementation! - Ellipse e(center(X), center(Y), ray(), ray(), 0); - return e.arc(initial, inner, final, _svg_compliant); + Ellipse e(_center[X], _center[Y], _radius, _radius, 0); + return e.arc(initial, inner, final); } -D2<SBasis> Circle::toSBasis() +bool Circle::operator==(Circle const &other) const +{ + if (_center != other._center) return false; + if (_radius != other._radius) return false; + return true; +} + +D2<SBasis> Circle::toSBasis() const { D2<SBasis> B; Linear bo = Linear(0, 2 * M_PI); @@ -108,27 +273,57 @@ D2<SBasis> Circle::toSBasis() B[0] = cos(bo,4); B[1] = sin(bo,4); - B = B * m_ray + m_centre; + B = B * _radius + _center; return B; } -void -Circle::getPath(std::vector<Path> &path_out) { - Path pb; - D2<SBasis> B = toSBasis(); +void Circle::fit(std::vector<Point> const& points) +{ + size_t sz = points.size(); + if (sz < 2) { + THROW_RANGEERROR("fitting error: too few points passed"); + } + if (sz == 2) { + _center = points[0] * 0.5 + points[1] * 0.5; + _radius = distance(points[0], points[1]) / 2; + return; + } - pb.append(SBasisCurve(B)); + NL::LFMCircle model; + NL::least_squeares_fitter<NL::LFMCircle> fitter(model, sz); - path_out.push_back(pb); -} + for (size_t i = 0; i < sz; ++i) { + fitter.append(points[i]); + } + fitter.update(); + NL::Vector z(sz, 0.0); + model.instance(*this, fitter.result(z)); +} -} // end namespace Geom +bool are_near(Circle const &a, Circle const &b, Coord eps) +{ + // to check whether no point on a is further than eps from b, + // we check two things: + // 1. if radii differ by more than eps, there is definitely a point that fails + // 2. if they differ by less, we check the centers. They have to be closer + // together if the radius differs, since the maximum distance will be + // equal to sum of radius difference and distance between centers. + if (!are_near(a.radius(), b.radius(), eps)) return false; + Coord adjusted_eps = eps - fabs(a.radius() - b.radius()); + return are_near(a.center(), b.center(), adjusted_eps); +} +std::ostream &operator<<(std::ostream &out, Circle const &c) +{ + out << "Circle(" << c.center() << ", " << format_coord_nice(c.radius()) << ")"; + return out; +} +} // end namespace Geom /* Local Variables: diff --git a/src/2geom/circle.h b/src/2geom/circle.h index ca9241047..a4d5f2097 100644 --- a/src/2geom/circle.h +++ b/src/2geom/circle.h @@ -1,11 +1,11 @@ -/** - * \file - * \brief Circles +/** @file + * @brief Circle shape *//* * Authors: - * Marco Cecchetti <mrcekets at gmail.com> + * Marco Cecchetti <mrcekets at gmail.com> + * Krzysztof Kosiński <tweenk.pl@gmail.com> * - * Copyright 2008 authors + * Copyright 2008-2014 Authors * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public @@ -34,83 +34,119 @@ #ifndef LIB2GEOM_SEEN_CIRCLE_H #define LIB2GEOM_SEEN_CIRCLE_H -#include <vector> +#include <2geom/forward.h> +#include <2geom/intersection.h> #include <2geom/point.h> -#include <2geom/exception.h> -#include <2geom/path.h> +#include <2geom/rect.h> +#include <2geom/transforms.h> namespace Geom { class EllipticalArc; +/** @brief Set of all points at a fixed distance from the center + * @ingroup Shapes */ class Circle + : boost::equality_comparable1< Circle + , MultipliableNoncommutative< Circle, Translate + , MultipliableNoncommutative< Circle, Rotate + , MultipliableNoncommutative< Circle, Zoom + > > > > { - public: - Circle() + Point _center; + Coord _radius; + +public: + Circle() {} + Circle(Coord cx, Coord cy, Coord r) + : _center(cx, cy), _radius(r) + {} + Circle(Point const ¢er, Coord r) + : _center(center), _radius(r) {} - Circle(double cx, double cy, double r) - : m_centre(cx, cy), m_ray(r) - { + Circle(Coord A, Coord B, Coord C, Coord D) { + setCoefficients(A, B, C, D); } - Circle(Point center, double r) - : m_centre(center), m_ray(r) - { - } + // Construct the unique circle passing through three points. + //Circle(Point const &a, Point const &b, Point const &c); - Circle(double A, double B, double C, double D) - { - set(A, B, C, D); - } + Point center() const { return _center; } + Coord center(Dim2 d) const { return _center[d]; } + Coord radius() const { return _radius; } + Coord area() const { return M_PI * _radius * _radius; } + bool isDegenerate() const { return _radius == 0; } - Circle(std::vector<Point> const& points) - { - set(points); - } + void setCenter(Point const &p) { _center = p; } + void setRadius(Coord c) { _radius = c; } - void set(double cx, double cy, double r) - { - m_centre[X] = cx; - m_centre[Y] = cy; - m_ray = r; - } + Rect boundsFast() const; + Rect boundsExact() const { return boundsFast(); } + + Point initialPoint() const; + Point finalPoint() const { return initialPoint(); } + Point pointAt(Coord t) const; + Coord valueAt(Coord t, Dim2 d) const; + Coord timeAt(Point const &p) const; + Coord nearestTime(Point const &p) const; + + bool contains(Point const &p) const { return distance(p, _center) <= _radius; } + bool contains(Rect const &other) const; + bool contains(Circle const &other) const; + bool intersects(Line const &l) const; + bool intersects(LineSegment const &l) const; + bool intersects(Circle const &other) const; + + std::vector<ShapeIntersection> intersect(Line const &other) const; + std::vector<ShapeIntersection> intersect(LineSegment const &other) const; + std::vector<ShapeIntersection> intersect(Circle const &other) const; // build a circle by its implicit equation: // Ax^2 + Ay^2 + Bx + Cy + D = 0 - void set(double A, double B, double C, double D); + void setCoefficients(Coord A, Coord B, Coord C, Coord D); + void coefficients(Coord &A, Coord &B, Coord &C, Coord &D) const; + std::vector<Coord> coefficients() const; - // build up the best fitting circle wrt the passed points - // prerequisite: at least 3 points must be passed - void set(std::vector<Point> const& points); + Zoom unitCircleTransform() const; + Zoom inverseUnitCircleTransform() const; EllipticalArc * - arc(Point const& initial, Point const& inner, Point const& final, - bool _svg_compliant = true); + arc(Point const& initial, Point const& inner, Point const& final) const; - D2<SBasis> toSBasis(); - void getPath(std::vector<Path> &path_out); + D2<SBasis> toSBasis() const; - Point center() const - { - return m_centre; + Circle &operator*=(Translate const &t) { + _center *= t; + return *this; } - - Coord center(Dim2 d) const - { - return m_centre[d]; + Circle &operator*=(Rotate const &) { + return *this; } - - Coord ray() const - { - return m_ray; + Circle &operator*=(Zoom const &z) { + _center *= z; + _radius *= z.scale(); + return *this; } + bool operator==(Circle const &other) const; + + /** @brief Fit the circle to the passed points using the least squares method. + * @param points Samples at the perimeter of the circle */ + void fit(std::vector<Point> const &points); +}; + +bool are_near(Circle const &a, Circle const &b, Coord eps=EPSILON); + +std::ostream &operator<<(std::ostream &out, Circle const &c); - private: - Point m_centre; - Coord m_ray; +template <> +struct ShapeTraits<Circle> { + typedef Coord TimeType; + typedef Interval IntervalType; + typedef Ellipse AffineClosureType; + typedef Intersection<> IntersectionType; }; } // end namespace Geom diff --git a/src/2geom/circulator.h b/src/2geom/circulator.h index 9671ce4a9..06e4d2c4e 100644 --- a/src/2geom/circulator.h +++ b/src/2geom/circulator.h @@ -1,5 +1,4 @@ -/** - * @file circulator.h +/** @file * @brief Circular iterator adapter *//* * Copyright 2006 MenTaLguY <mental@rydia.net> @@ -29,8 +28,8 @@ * */ -#ifndef SEEN_Circulator_H -#define SEEN_Circulator_H +#ifndef LIB2GEOM_SEEN_CIRCULATOR_H +#define LIB2GEOM_SEEN_CIRCULATOR_H #include <iterator> @@ -137,7 +136,7 @@ Geom::Circulator<T> operator+(int n, Geom::Circulator<T> const &c) { return c + n; } -#endif // SEEN_Circulator_H +#endif // LIB2GEOM_SEEN_CIRCULATOR_H /* Local Variables: diff --git a/src/2geom/concepts.h b/src/2geom/concepts.h index c89c3a224..c331f078b 100644 --- a/src/2geom/concepts.h +++ b/src/2geom/concepts.h @@ -35,9 +35,11 @@ #include <2geom/interval.h> #include <2geom/point.h> #include <2geom/rect.h> +#include <2geom/intersection.h> #include <vector> -#include <boost/concept_check.hpp> +#include <boost/concept/assert.hpp> #include <2geom/forward.h> +#include <2geom/transforms.h> namespace Geom { @@ -71,16 +73,18 @@ struct FragmentConcept { SbType sb; void constraints() { t = T(o); - b = t.isZero(); - b = t.isConstant(); + b = t.isZero(d); + b = t.isConstant(d); b = t.isFinite(); o = t.at0(); o = t.at1(); + t.at0() = o; + t.at1() = o; o = t.valueAt(d); o = t(d); v = t.valueAndDerivatives(d, u-1); - //Is a pure derivative (ignoring others) accessor ever much faster? - //u = number of values returned. first val is value. + //Is a pure derivative (ignoring others) accessor ever much faster? + //u = number of values returned. first val is value. sb = t.toSBasis(); t = reverse(t); i = bounds_fast(t); @@ -97,9 +101,51 @@ struct FragmentConcept { }; template <typename T> +struct ShapeConcept { + typedef typename ShapeTraits<T>::TimeType Time; + typedef typename ShapeTraits<T>::IntervalType Interval; + typedef typename ShapeTraits<T>::AffineClosureType AffineClosure; + typedef typename ShapeTraits<T>::IntersectionType Isect; + + T shape, other; + Time t; + Point p; + AffineClosure ac; + Affine m; + Translate tr; + Coord c; + bool bool_; + std::vector<Isect> ivec; + + void constraints() { + p = shape.pointAt(t); + c = shape.valueAt(t, X); + ivec = shape.intersect(other); + t = shape.nearestTime(p); + shape *= tr; + ac = shape; + ac *= m; + bool_ = (shape == shape); + bool_ = (shape != other); + bool_ = shape.isDegenerate(); + //bool_ = are_near(shape, other, c); + } +}; + +template <typename T> inline T portion(const T& t, const Interval& i) { return portion(t, i.min(), i.max()); } template <typename T> +struct EqualityComparableConcept { + T a, b; + bool bool_; + void constaints() { + bool_ = (a == b); + bool_ = (a != b); + } +}; + +template <typename T> struct NearConcept { T a, b; double tol; diff --git a/src/2geom/conic_section_clipper.h b/src/2geom/conic_section_clipper.h index a02cda4d3..38bba338e 100644 --- a/src/2geom/conic_section_clipper.h +++ b/src/2geom/conic_section_clipper.h @@ -1,7 +1,6 @@ -/** - * \file - * \brief Conic section clipping with respect to a rectangle - * +/** @file + * @brief Conic section clipping with respect to a rectangle + *//* * Authors: * Marco Cecchetti <mrcekets at gmail> * @@ -34,8 +33,8 @@ -#ifndef _2GEOM_CONIC_SECTION_CLIPPER_H_ -#define _2GEOM_CONIC_SECTION_CLIPPER_H_ +#ifndef LIB2GEOM_SEEN_CONIC_SECTION_CLIPPER_H +#define LIB2GEOM_SEEN_CONIC_SECTION_CLIPPER_H #undef CLIP_WITH_CAIRO_SUPPORT diff --git a/src/2geom/conic_section_clipper_cr.h b/src/2geom/conic_section_clipper_cr.h index 31f5a4269..6c62494de 100644 --- a/src/2geom/conic_section_clipper_cr.h +++ b/src/2geom/conic_section_clipper_cr.h @@ -1,7 +1,6 @@ -/** - * \file - * \brief Conic section clipping with respect to a rectangle - * +/** @file + * @brief Conic section clipping with respect to a rectangle + *//* * Authors: * Marco Cecchetti <mrcekets at gmail> * @@ -39,8 +38,8 @@ //////////////////////////////////////////////////////////////////////////////// -#ifndef _2GEOM_CONIC_SECTION_CLIPPER_CR_H_ -#define _2GEOM_CONIC_SECTION_CLIPPER_CR_H_ +#ifndef LIB2GEOM_SEEN_CONIC_SECTION_CLIPPER_CR_H +#define LIB2GEOM_SEEN_CONIC_SECTION_CLIPPER_CR_H #define CLIP_WITH_CAIRO_SUPPORT diff --git a/src/2geom/conic_section_clipper_impl.cpp b/src/2geom/conic_section_clipper_impl.cpp index 2867e243c..c57307974 100644 --- a/src/2geom/conic_section_clipper_impl.cpp +++ b/src/2geom/conic_section_clipper_impl.cpp @@ -173,7 +173,7 @@ bool CLIPPER_CLASS::intersect (std::vector<Point> & crossing_points) const cpts.size()) // remove duplicates - std::sort (cpts.begin(), cpts.end(), Point::LexOrder<X>()); + std::sort (cpts.begin(), cpts.end(), Point::LexLess<X>()); cpts.erase (std::unique (cpts.begin(), cpts.end()), cpts.end()); @@ -203,7 +203,7 @@ bool CLIPPER_CLASS::intersect (std::vector<Point> & crossing_points) const inline double signed_triangle_area (Point const& p1, Point const& p2, Point const& p3) { - return (cross(p3, p2) - cross(p3, p1) + cross(p2, p1)); + return (cross(p2, p3) - cross(p1, p3) + cross(p1, p2)); } @@ -216,6 +216,8 @@ double signed_triangle_area (Point const& p1, Point const& p2, Point const& p3) */ bool CLIPPER_CLASS::are_paired (Point& M, const Point & P1, const Point & P2) const { + using std::swap; + /* * we looks for the points on the conic whose tangent is parallel to the * arc chord P1P2, they will be extrema of the conic arc P1P2 wrt the @@ -257,9 +259,8 @@ bool CLIPPER_CLASS::are_paired (Point& M, const Point & P1, const Point & P2) co if (sgn(side0) == sgn(side1)) { - if (std::fabs(side0) > std::fabs(side1)) - { - std::swap (extrema[0], extrema[1]); + if (std::fabs(side0) > std::fabs(side1)) { + swap(extrema[0], extrema[1]); } extrema.pop_back(); } @@ -371,6 +372,8 @@ void CLIPPER_CLASS::pairing (std::vector<Point> & paired_points, */ bool CLIPPER_CLASS::clip (std::vector<RatQuad> & arcs) { + using std::swap; + arcs.clear(); std::vector<Point> crossing_points; std::vector<Point> paired_points; @@ -454,14 +457,14 @@ bool CLIPPER_CLASS::clip (std::vector<RatQuad> & arcs) double angle = cs.axis_angle(); Line axis1 (*c, angle); rts = cs.roots (axis1); - if (rts[0] > rts[1]) std::swap (rts[0], rts[1]); + if (rts[0] > rts[1]) swap (rts[0], rts[1]); paired_points[0] = axis1.pointAt (rts[0]); paired_points[1] = axis1.pointAt (rts[1]); paired_points[2] = paired_points[1]; paired_points[3] = paired_points[0]; Line axis2 (*c, angle + M_PI/2); rts = cs.roots (axis2); - if (rts[0] > rts[1]) std::swap (rts[0], rts[1]); + if (rts[0] > rts[1]) swap (rts[0], rts[1]); inner_points.push_back (axis2.pointAt (rts[0])); inner_points.push_back (axis2.pointAt (rts[1])); } diff --git a/src/2geom/conic_section_clipper_impl.h b/src/2geom/conic_section_clipper_impl.h index 37415df97..e38a6d416 100644 --- a/src/2geom/conic_section_clipper_impl.h +++ b/src/2geom/conic_section_clipper_impl.h @@ -1,7 +1,6 @@ -/** - * \file - * \brief Conic section clipping with respect to a rectangle - * +/** @file + * @brief Conic section clipping with respect to a rectangle + *//* * Authors: * Marco Cecchetti <mrcekets at gmail> * @@ -31,11 +30,8 @@ * the specific language governing rights and limitations. */ - - - -#ifndef _2GEOM_CONIC_SECTION_CLIPPER_IMPL_H_ -#define _2GEOM_CONIC_SECTION_CLIPPER_IMPL_H_ +#ifndef LIB2GEOM_SEEN_CONIC_SECTION_CLIPPER_IMPL_H +#define LIB2GEOM_SEEN_CONIC_SECTION_CLIPPER_IMPL_H #include <2geom/conicsec.h> @@ -336,13 +332,7 @@ void CLIPPER_CLASS::rsplit (std::list<Point> & points, } // end namespace Geom - - - -#endif // _2GEOM_CONIC_SECTION_CLIPPER_IMPL_H_ - - - +#endif // LIB2GEOM_SEEN_CONIC_SECTION_CLIPPER_IMPL_H /* Local Variables: diff --git a/src/2geom/conicsec.cpp b/src/2geom/conicsec.cpp index 367dc2503..089db71a4 100644 --- a/src/2geom/conicsec.cpp +++ b/src/2geom/conicsec.cpp @@ -40,44 +40,16 @@ #include <sstream> #include <stdexcept> - - - - namespace Geom { LineSegment intersection(Line l, Rect r) { - Point p0, p1; - double a,b,c; - std::vector<double> ifc = l.coefficients(); - a = ifc[0]; - b = ifc[1]; - c = ifc[2]; - if (fabs(b) > fabs(a)) { - p0 = Point(r[0][0], (-c - a*r[0][0])/b); - if (p0[1] < r[1][0]) - p0 = Point((-c - b*r[1][0])/a, r[1][0]); - if (p0[1] > r[1][1]) - p0 = Point((-c - b*r[1][1])/a, r[1][1]); - p1 = Point(r[0][1], (-c - a*r[0][1])/b); - if (p1[1] < r[1][0]) - p1 = Point((-c - b*r[1][0])/a, r[1][0]); - if (p1[1] > r[1][1]) - p1 = Point((-c - b*r[1][1])/a, r[1][1]); + boost::optional<LineSegment> seg = l.clip(r); + if (seg) { + return *seg; } else { - p0 = Point((-c - b*r[1][0])/a, r[1][0]); - if (p0[0] < r[0][0]) - p0 = Point(r[0][0], (-c - a*r[0][0])/b); - if (p0[0] > r[0][1]) - p0 = Point(r[0][1], (-c - a*r[0][1])/b); - p1 = Point((-c - b*r[1][1])/a, r[1][1]); - if (p1[0] < r[0][0]) - p1 = Point(r[0][0], (-c - a*r[0][0])/b); - if (p1[0] > r[0][1]) - p1 = Point(r[0][1], (-c - a*r[0][1])/b); + return LineSegment(Point(0,0), Point(0,0)); } - return LineSegment(p0, p1); } static double det(Point a, Point b) { @@ -108,41 +80,6 @@ static double boxprod(Point a, Point b, Point c) { return det(a,b) - det(a,c) + det(b,c); } - -/** - * Find the roots of (q2x + q1)x+q0 = 0 - * Tries to be numerically robust. - */ -template <typename T> -static std::vector<T> quadratic_roots(T q0, T q1, T q2) { - std::vector<double> r; - if(q2 == 0) { - if(q1 == 0) { // zero or infinite roots - return r; - } - r.push_back(-q0/q1); - } else { - double desc = q1*q1 - 4*q2*q0; - /*cout << q2 << ", " - << q1 << ", " - << q0 << "; " - << desc << "\n";*/ - if (desc < 0) - return r; - else if (desc == 0) - r.push_back(-q1/(2*q2)); - else { - desc = std::sqrt(desc); - double t = -0.5*(q1+sgn(q1)*desc); - r.push_back(t/q2); - r.push_back(q0/t); - } - } - return r; -} - - - class BadConversion : public std::runtime_error { public: BadConversion(const std::string& s) @@ -673,7 +610,7 @@ std::vector<double> xAx::roots(Line const &l) const { Interval xAx::quad_ex(double a, double b, double c, Interval ivl) { double cx = -b*0.5/a; - Interval bnds((a*ivl[0]+b)*ivl[0]+c, (a*ivl[1]+b)*ivl[1]+c); + Interval bnds((a*ivl.min()+b)*ivl.min()+c, (a*ivl.max()+b)*ivl.max()+c); if(ivl.contains(cx)) bnds.expandTo((a*cx+b)*cx+c); return bnds; @@ -714,14 +651,14 @@ Interval xAx::extrema(Rect r) const { ext |= Interval(valueAt(r.corner(i))); return ext; } - double k = r[0][0]; - Interval ext = quad_ex(c[2], c[1]*k+c[4], (c[0]*k + c[3])*k + c[5], r[1]); - k = r[0][1]; - ext |= quad_ex(c[2], c[1]*k+c[4], (c[0]*k + c[3])*k + c[5], r[1]); - k = r[1][0]; - ext |= quad_ex(c[0], c[1]*k+c[3], (c[2]*k + c[4])*k + c[5], r[0]); - k = r[1][1]; - ext |= quad_ex(c[0], c[1]*k+c[3], (c[2]*k + c[4])*k + c[5], r[0]); + double k = r[X].min(); + Interval ext = quad_ex(c[2], c[1]*k+c[4], (c[0]*k + c[3])*k + c[5], r[Y]); + k = r[X].max(); + ext |= quad_ex(c[2], c[1]*k+c[4], (c[0]*k + c[3])*k + c[5], r[Y]); + k = r[Y].min(); + ext |= quad_ex(c[0], c[1]*k+c[3], (c[2]*k + c[4])*k + c[5], r[X]); + k = r[Y].max(); + ext |= quad_ex(c[0], c[1]*k+c[3], (c[2]*k + c[4])*k + c[5], r[X]); boost::optional<Point> B0 = bottom(); if (B0 && r.contains(*B0)) ext.expandTo(0); @@ -753,7 +690,7 @@ bool at_infinity (Point const& p) inline double signed_triangle_area (Point const& p1, Point const& p2, Point const& p3) { - return (cross(p3, p2) - cross(p3, p1) + cross(p2, p1)); + return (cross(p2, p3) - cross(p1, p3) + cross(p1, p2)); } @@ -801,6 +738,8 @@ void xAx::set(std::vector<Point> const& points) */ void xAx::set (const Point& _vertex, double _angle, double _dist1, double _dist2) { + using std::swap; + if (_dist2 == infinity() || _dist2 == -infinity()) // parabola { if (_dist1 == infinity()) // degenerate to a line @@ -842,7 +781,7 @@ void xAx::set (const Point& _vertex, double _angle, double _dist1, double _dist2 if (std::fabs(_dist1) > std::fabs(_dist2)) { - std::swap (_dist1, _dist2); + swap (_dist1, _dist2); } if (_dist1 < 0) { @@ -1398,6 +1337,7 @@ bool xAx::decompose (Line& l1, Line& l2) const */ Rect xAx::arc_bound (const Point & P1, const Point & Q, const Point & P2) const { + using std::swap; //std::cout << "BOUND: P1 = " << P1 << std::endl; //std::cout << "BOUND: Q = " << Q << std::endl; //std::cout << "BOUND: P2 = " << P2 << std::endl; @@ -1442,38 +1382,35 @@ Rect xAx::arc_bound (const Point & P1, const Point & Q, const Point & P2) const if (sgn(Mside) == sgn(Qside)) { //std::cout << "BOUND: M.size() == 1" << std::endl; - if (M[0][dim] > B[dim][1]) - B[dim][1] = M[0][dim]; - else if (M[0][dim] < B[dim][0]) - B[dim][0] = M[0][dim]; + B[dim].expandTo(M[0][dim]); } } else if (M.size() == 2) { //std::cout << "BOUND: M.size() == 2" << std::endl; if (M[0][dim] > M[1][dim]) - std::swap (M[0], M[1]); + swap (M[0], M[1]); - if (M[0][dim] > B[dim][1]) + if (M[0][dim] > B[dim].max()) { double Mside = signed_triangle_area (P1, M[0], P2); if (sgn(Mside) == sgn(Qside)) - B[dim][1] = M[0][dim]; + B[dim].setMax(M[0][dim]); } - else if (M[1][dim] < B[dim][0]) + else if (M[1][dim] < B[dim].min()) { double Mside = signed_triangle_area (P1, M[1], P2); if (sgn(Mside) == sgn(Qside)) - B[dim][0] = M[1][dim]; + B[dim].setMin(M[1][dim]); } else { double Mside = signed_triangle_area (P1, M[0], P2); if (sgn(Mside) == sgn(Qside)) - B[dim][0] = M[0][dim]; + B[dim].setMin(M[0][dim]); Mside = signed_triangle_area (P1, M[1], P2); if (sgn(Mside) == sgn(Qside)) - B[dim][1] = M[1][dim]; + B[dim].setMax(M[1][dim]); } } } @@ -1486,7 +1423,7 @@ Rect xAx::arc_bound (const Point & P1, const Point & Q, const Point & P2) const * * P: the point to compute the nearest one */ -std::vector<Point> xAx::allNearestPoints (const Point &P) const +std::vector<Point> xAx::allNearestTimes (const Point &P) const { // TODO: manage the circle - centre case std::vector<Point> points; diff --git a/src/2geom/conicsec.h b/src/2geom/conicsec.h index ec9a430d9..dbe564872 100644 --- a/src/2geom/conicsec.h +++ b/src/2geom/conicsec.h @@ -1,7 +1,6 @@ -/** - * \file - * \brief Conic Section - * +/** @file + * @brief Conic Section + *//* * Authors: * Nathan Hurst <njh@njhurst.com> * @@ -32,8 +31,8 @@ */ -#ifndef _2GEOM_CONIC_SECTION_H_ -#define _2GEOM_CONIC_SECTION_H_ +#ifndef LIB2GEOM_SEEN_CONICSEC_H +#define LIB2GEOM_SEEN_CONICSEC_H #include <2geom/exception.h> #include <2geom/angle.h> @@ -463,33 +462,28 @@ public: bool arc_contains (const Point & _point, const Point & _initial, const Point & _inner, const Point & _final) const { - double pa = angle_at (_point); - double sa = angle_at (_initial); - double ia = angle_at (_inner); - double ea = angle_at (_final); - // we test if _point and _inner have the same position - // wrt _initial and _final - return Geom::arc_contains (pa, sa, ia, ea); + AngleInterval ai(angle_at(_initial), angle_at(_inner), angle_at(_final)); + return ai.contains(angle_at(_point)); } Rect arc_bound (const Point & P1, const Point & Q, const Point & P2) const; - std::vector<Point> allNearestPoints (const Point &P) const; + std::vector<Point> allNearestTimes (const Point &P) const; /* * Return the point on the conic section nearest to the passed point "P". * * P: the point to compute the nearest one */ - Point nearestPoint (const Point &P) const + Point nearestTime (const Point &P) const { - std::vector<Point> points = allNearestPoints (P); + std::vector<Point> points = allNearestTimes (P); if ( !points.empty() ) { return points.front(); } // else - THROW_LOGICALERROR ("nearestPoint: no nearest point found"); + THROW_LOGICALERROR ("nearestTime: no nearest point found"); return Point(); } @@ -509,8 +503,7 @@ inline std::ostream &operator<< (std::ostream &out_file, const xAx &x) { }; -#endif // _2GEOM_CONIC_SECTION_H_ - +#endif // LIB2GEOM_SEEN_CONICSEC_H /* Local Variables: diff --git a/src/2geom/conjugate_gradient.cpp b/src/2geom/conjugate_gradient.cpp deleted file mode 100644 index 588513414..000000000 --- a/src/2geom/conjugate_gradient.cpp +++ /dev/null @@ -1,143 +0,0 @@ -/* - * conjugate_gradient.cpp - * - * Copyright 2006 Nathan Hurst <njh@mail.csse.monash.edu.au> - * - * This library is free software; you can redistribute it and/or - * modify it either under the terms of the GNU Lesser General Public - * License version 2.1 as published by the Free Software Foundation - * (the "LGPL") or, at your option, under the terms of the Mozilla - * Public License Version 1.1 (the "MPL"). If you do not alter this - * notice, a recipient may use your version of this file under either - * the MPL or the LGPL. - * - * You should have received a copy of the LGPL along with this library - * in the file COPYING-LGPL-2.1; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * You should have received a copy of the MPL along with this library - * in the file COPYING-MPL-1.1 - * - * The contents of this file are subject to the Mozilla Public License - * Version 1.1 (the "License"); you may not use this file except in - * compliance with the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY - * OF ANY KIND, either express or implied. See the LGPL or the MPL for - * the specific language governing rights and limitations. - * - */ - -#include <math.h> -#include <stdlib.h> -#include <valarray> -#include <cassert> -#include <2geom/conjugate_gradient.h> - -/* lifted wholely from wikipedia. */ - -namespace Geom -{ - -using std::valarray; - -static void -matrix_times_vector(valarray<double> const &matrix, /* m * n */ - valarray<double> const &vec, /* n */ - valarray<double> &result) /* m */ -{ - unsigned n = vec.size(); - unsigned m = result.size(); - assert(m*n == matrix.size()); - const double* mp = &const_cast<valarray<double>&>(matrix)[0]; - for (unsigned i = 0; i < m; i++) { - double res = 0; - for (unsigned j = 0; j < n; j++) - res += *mp++ * vec[j]; - result[i] = res; - } -} - -/** -// only used in commented code below -static double Linfty(valarray<double> const &vec) { - return std::max(vec.max(), -vec.min()); -} -**/ - -double -inner(valarray<double> const &x, - valarray<double> const &y) { - double total = 0; - for(unsigned i = 0; i < x.size(); i++) - total += x[i]*y[i]; - return total;// (x*y).sum(); <- this is more concise, but ineff -} - -void -conjugate_gradient(double **A, - double *x, - double *b, - unsigned n, - double tol, - int max_iterations, - bool ortho1) { - valarray<double> vA(n*n); - valarray<double> vx(n); - valarray<double> vb(n); - for(unsigned i=0;i<n;i++) { - vx[i]=x[i]; - vb[i]=b[i]; - for(unsigned j=0;j<n;j++) { - vA[i*n+j]=A[i][j]; - } - } - conjugate_gradient(vA,vx,vb,n,tol,max_iterations,ortho1); - for(unsigned i=0;i<n;i++) { - x[i]=vx[i]; - } -} -void -conjugate_gradient(valarray<double> const &A, - valarray<double> &x, - valarray<double> const &b, - unsigned n, double tol, - unsigned max_iterations, bool /*ortho1*/) { - valarray<double> Ap(n), p(n), r(n); - matrix_times_vector(A,x,Ap); - r=b-Ap; - double r_r = inner(r,r); - unsigned k = 0; - tol *= tol; - while(k < max_iterations && r_r > tol) { - k++; - double r_r_new = r_r; - if(k == 1) - p = r; - else { - r_r_new = inner(r,r); - p = r + (r_r_new/r_r)*p; - } - matrix_times_vector(A, p, Ap); - double alpha_k = r_r_new / inner(p, Ap); - x += alpha_k*p; - r -= alpha_k*Ap; - r_r = r_r_new; - } - //printf("njh: %d iters, Linfty = %g L2 = %g\n", k, - //std::max(-r.min(), r.max()), sqrt(r_r)); - // x is solution -} - -} // namespace Geom - -/* - 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/2geom/convex-cover.h b/src/2geom/convex-cover.h deleted file mode 100644 index d290b7e80..000000000 --- a/src/2geom/convex-cover.h +++ /dev/null @@ -1,204 +0,0 @@ -/** - * \file - * \brief Dynamic convex hull structure - * - * Copyright 2006 Nathan Hurst <njh@mail.csse.monash.edu.au> - * Copyright 2006 Michael G. Sloan <mgsloan@gmail.com> - * - * This library is free software; you can redistribute it and/or - * modify it either under the terms of the GNU Lesser General Public - * License version 2.1 as published by the Free Software Foundation - * (the "LGPL") or, at your option, under the terms of the Mozilla - * Public License Version 1.1 (the "MPL"). If you do not alter this - * notice, a recipient may use your version of this file under either - * the MPL or the LGPL. - * - * You should have received a copy of the LGPL along with this library - * in the file COPYING-LGPL-2.1; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * You should have received a copy of the MPL along with this library - * in the file COPYING-MPL-1.1 - * - * The contents of this file are subject to the Mozilla Public License - * Version 1.1 (the "License"); you may not use this file except in - * compliance with the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY - * OF ANY KIND, either express or implied. See the LGPL or the MPL for - * the specific language governing rights and limitations. - * - */ - -#ifndef GEOM_CONVEX_COVER_H -#define GEOM_CONVEX_COVER_H - -#include <2geom/point.h> -#include <vector> - -namespace Geom{ - -/* A convex cover is a sequence of convex polygons that completely cover the path. For now a - * convex hull class is included here (the convex-hull header is wrong) - */ - -/** ConvexHull - * A convexhull is a convex region - every point between two points in the convex hull is also in - * the convex hull. It is defined by a set of points travelling in a clockwise direction. We require the first point to be top most, and of the topmost, leftmost. - - * An empty hull has no points, we allow a single point or two points degenerate cases. - - * We could provide the centroid as a member for efficient direction determination. We can update the - * centroid with all operations with the same time complexity as the operation. - */ - -class ConvexHull{ -public: // XXX: should be private :) - // extracts the convex hull of boundary. internal use only - void find_pivot(); - void angle_sort(); - void graham_scan(); - void andrew_scan(); - void graham(); -public: - std::vector<Point> boundary; - //Point centroid; - - void merge(Point p); - bool contains_point(Point p); - bool strict_contains_point(Point p); - - inline size_t size() const { return boundary.size();} - inline Point operator[](int i) const { - - int l = boundary.size(); - if(l == 0) return Point(); - return boundary[i >= 0 ? i % l : (i % l) + l]; - } - - /*inline Point &operator[](unsigned i) { - int l = boundary.size(); - if(l == 0) return Point(); - return boundary[i >= 0 ? i % l : i % l + l]; - }*/ - -public: - ConvexHull() {} - ConvexHull(std::vector<Point> const & points) : - boundary (points) - { - graham(); - } - - template <typename T> - ConvexHull(T b, T e) :boundary(b,e) {} - - ~ConvexHull() - { - } - -public: - /** Is the convex hull clockwise? We use the definition of clockwise from point.h - **/ - bool is_clockwise() const; - bool top_point_first() const; - bool meets_invariants() const; - - // contains no points - bool empty() const { return boundary.empty();} - - // contains exactly one point - bool singular() const { return boundary.size() == 1;} - - // all points are on a line - bool linear() const { return boundary.size() == 2;} - bool is_degenerate() const; - - // area of the convex hull - double centroid_and_area(Geom::Point& centroid) const; - double area() const { - Point tmp; - return centroid_and_area(tmp); - } - - // furthest point in a direction (lg time) - Point const * furthest(Point direction) const; - - bool is_left(Point p, int n); - bool is_strict_left(Point p, int n); - int find_left(Point p); - int find_strict_left(Point p); - double narrowest_diameter(Point &a, Point &b, Point &c); - -}; -/** @brief Output operator for points. - * Prints out all the coordinates. */ -inline std::ostream &operator<< (std::ostream &out_file, const Geom::ConvexHull &in_cvx) { - out_file << "ConvexHull("; - for(unsigned i = 0; i < in_cvx.size(); i++) { - out_file << in_cvx.boundary[i] << ", "; - } - out_file << ")"; - return out_file; -} - -// do two convex hulls intersect? -bool intersectp(ConvexHull a, ConvexHull b); - -std::vector<std::pair<int, int> > bridges(ConvexHull a, ConvexHull b); - -// find the convex hull intersection -ConvexHull intersection(ConvexHull a, ConvexHull b); -ConvexHull sweepline_intersection(ConvexHull const &a, ConvexHull const &b); - -// find the convex hull of a set of convex hulls -ConvexHull merge(ConvexHull a, ConvexHull b); - -// naive approach -ConvexHull graham_merge(ConvexHull a, ConvexHull b); - -// naive approach -ConvexHull andrew_merge(ConvexHull a, ConvexHull b); - -unsigned find_bottom_right(ConvexHull const &a); - -/*** Arbitrary transform operator. - * Take a convex hull and apply an arbitrary convexity preserving transform. - * we should be concerned about singular tranforms here. - */ -template <class T> ConvexHull operator*(ConvexHull const &p, T const &m) { - ConvexHull pr; - - pr.boundary.reserve(p.boundary.size()); - - for(unsigned i = 0; i < p.boundary.size(); i++) { - pr.boundary.push_back(p.boundary[i]*m); - } - return pr; -} - -ConvexHull clip(ConvexHull const & ch, Point n, double d); - -//TODO: reinstate -/*class ConvexCover{ -public: - Path const* path; - std::vector<ConvexHull> cc; - - ConvexCover(Path const &sp); -};*/ - -}; - -#endif //2GEOM_CONVEX_COVER_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/2geom/convex-cover.cpp b/src/2geom/convex-hull.cpp index 5e599fdde..4f5e06733 100644 --- a/src/2geom/convex-cover.cpp +++ b/src/2geom/convex-hull.cpp @@ -1,8 +1,11 @@ -/* - * convex-cover.cpp - * - * Copyright 2006 Nathan Hurst <njh@mail.csse.monash.edu.au> - * Copyright 2006 Michael G. Sloan <mgsloan@gmail.com> +/** @file + * @brief Convex hull of a set of points + *//* + * Authors: + * Nathan Hurst <njh@mail.csse.monash.edu.au> + * Michael G. Sloan <mgsloan@gmail.com> + * Krzysztof Kosiński <tweenk.pl@gmail.com> + * Copyright 2006-2015 Authors * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public @@ -29,11 +32,13 @@ * */ -#include <2geom/convex-cover.h> +#include <2geom/convex-hull.h> #include <2geom/exception.h> #include <algorithm> #include <map> -#include <assert.h> +#include <iostream> +#include <cassert> +#include <boost/array.hpp> /** Todo: + modify graham scan to work top to bottom, rather than around angles @@ -50,9 +55,237 @@ using std::vector; using std::map; using std::pair; using std::make_pair; +using std::swap; + +namespace Geom { + +ConvexHull::ConvexHull(Point const &a, Point const &b) + : _boundary(2) + , _lower(0) +{ + _boundary[0] = a; + _boundary[1] = b; + std::sort(_boundary.begin(), _boundary.end(), Point::LexLess<X>()); + _construct(); +} + +ConvexHull::ConvexHull(Point const &a, Point const &b, Point const &c) + : _boundary(3) + , _lower(0) +{ + _boundary[0] = a; + _boundary[1] = b; + _boundary[2] = c; + std::sort(_boundary.begin(), _boundary.end(), Point::LexLess<X>()); + _construct(); +} + +ConvexHull::ConvexHull(Point const &a, Point const &b, Point const &c, Point const &d) + : _boundary(4) + , _lower(0) +{ + _boundary[0] = a; + _boundary[1] = b; + _boundary[2] = c; + _boundary[3] = d; + std::sort(_boundary.begin(), _boundary.end(), Point::LexLess<X>()); + _construct(); +} + +ConvexHull::ConvexHull(std::vector<Point> const &pts) + : _lower(0) +{ + //if (pts.size() > 16) { // arbitrary threshold + // _prune(pts.begin(), pts.end(), _boundary); + //} else { + _boundary = pts; + std::sort(_boundary.begin(), _boundary.end(), Point::LexLess<X>()); + //} + _construct(); +} + +bool ConvexHull::_is_clockwise_turn(Point const &a, Point const &b, Point const &c) +{ + if (b == c) return false; + return cross(b-a, c-a) > 0; +} + +void ConvexHull::_construct() +{ + // _boundary must already be sorted in LexLess<X> order + if (_boundary.empty()) { + _lower = 0; + return; + } + if (_boundary.size() == 1 || (_boundary.size() == 2 && _boundary[0] == _boundary[1])) { + _boundary.resize(1); + _lower = 1; + return; + } + if (_boundary.size() == 2) { + _lower = 2; + return; + } + + std::size_t k = 2; + for (std::size_t i = 2; i < _boundary.size(); ++i) { + while (k >= 2 && !_is_clockwise_turn(_boundary[k-2], _boundary[k-1], _boundary[i])) { + --k; + } + std::swap(_boundary[k++], _boundary[i]); + } + + _lower = k; + std::sort(_boundary.begin() + k, _boundary.end(), Point::LexGreater<X>()); + _boundary.push_back(_boundary.front()); + for (std::size_t i = _lower; i < _boundary.size(); ++i) { + while (k > _lower && !_is_clockwise_turn(_boundary[k-2], _boundary[k-1], _boundary[i])) { + --k; + } + std::swap(_boundary[k++], _boundary[i]); + } + + _boundary.resize(k-1); +} + +double ConvexHull::area() const +{ + if (size() <= 2) return 0; + + double a = 0; + for (std::size_t i = 0; i < size()-1; ++i) { + a += cross(_boundary[i], _boundary[i+1]); + } + a += cross(_boundary.back(), _boundary.front()); + return fabs(a * 0.5); +} + +OptRect ConvexHull::bounds() const +{ + OptRect ret; + if (empty()) return ret; + ret = Rect(left(), top(), right(), bottom()); + return ret; +} + +Point ConvexHull::topPoint() const +{ + Point ret; + ret[Y] = std::numeric_limits<Coord>::infinity(); + + for (UpperIterator i = upperHull().begin(); i != upperHull().end(); ++i) { + if (ret[Y] >= i->y()) { + ret = *i; + } else { + break; + } + } + + return ret; +} + +Point ConvexHull::bottomPoint() const +{ + Point ret; + ret[Y] = -std::numeric_limits<Coord>::infinity(); + + for (LowerIterator j = lowerHull().begin(); j != lowerHull().end(); ++j) { + if (ret[Y] <= j->y()) { + ret = *j; + } else { + break; + } + } + + return ret; +} + +template <typename Iter, typename Lex> +bool below_x_monotonic_polyline(Point const &p, Iter first, Iter last, Lex lex) +{ + typename Lex::Secondary above; + Iter f = std::lower_bound(first, last, p, lex); + if (f == last) return false; + if (f == first) { + if (p == *f) return true; + return false; + } + + Point a = *(f-1), b = *f; + if (a[X] == b[X]) { + if (above(p[Y], a[Y]) || above(b[Y], p[Y])) return false; + } else { + // TODO: maybe there is a more numerically stable method + Coord y = lerp((p[X] - a[X]) / (b[X] - a[X]), a[Y], b[Y]); + if (above(p[Y], y)) return false; + } + return true; +} + +bool ConvexHull::contains(Point const &p) const +{ + if (_boundary.empty()) return false; + if (_boundary.size() == 1) { + if (_boundary[0] == p) return true; + return false; + } + + // 1. verify that the point is in the relevant X range + if (p[X] < _boundary[0][X] || p[X] > _boundary[_lower-1][X]) return false; + + // 2. check whether it is below the upper hull + UpperIterator ub = upperHull().begin(), ue = upperHull().end(); + if (!below_x_monotonic_polyline(p, ub, ue, Point::LexLess<X>())) return false; + + // 3. check whether it is above the lower hull + LowerIterator lb = lowerHull().begin(), le = lowerHull().end(); + if (!below_x_monotonic_polyline(p, lb, le, Point::LexGreater<X>())) return false; + + return true; +} + +bool ConvexHull::contains(Rect const &r) const +{ + for (unsigned i = 0; i < 4; ++i) { + if (!contains(r.corner(i))) return false; + } + return true; +} + +bool ConvexHull::contains(ConvexHull const &ch) const +{ + // TODO: requires interiorContains. + // We have to check all points of ch, and each point takes logarithmic time. + // If there are more points in ch that here, it is faster to make the check + // the other way around. + /*if (ch.size() > size()) { + for (iterator i = begin(); i != end(); ++i) { + if (ch.interiorContains(*i)) return false; + } + return true; + }*/ + + for (iterator i = ch.begin(); i != ch.end(); ++i) { + if (!contains(*i)) return false; + } + return true; +} + +void ConvexHull::swap(ConvexHull &other) +{ + _boundary.swap(other._boundary); + std::swap(_lower, other._lower); +} -namespace Geom{ +void ConvexHull::swap(std::vector<Point> &pts) +{ + _boundary.swap(pts); + _lower = 0; + std::sort(_boundary.begin(), _boundary.end(), Point::LexLess<X>()); + _construct(); +} +#if 0 /*** SignedTriangleArea * returns the area of the triangle defined by p0, p1, p2. A clockwise triangle has positive area. */ @@ -118,147 +351,6 @@ public: #endif }; -void -ConvexHull::find_pivot() { - // Find pivot P; - unsigned pivot = 0; - for (unsigned i = 1; i < boundary.size(); i++) - if(boundary[i] <= boundary[pivot]) - pivot = i; - - std::swap(boundary[0], boundary[pivot]); -} - -void -ConvexHull::angle_sort() { -// sort points by angle (resolve ties in favor of point farther from P); -// we leave the first one in place as our pivot - std::sort(boundary.begin()+1, boundary.end(), angle_cmp(boundary[0])); -} - - -void -ConvexHull::graham_scan() { - // prune out equal points. points are sorted, so equals are adjacent - std::vector<Point>::iterator e = - std::unique(boundary.begin(), boundary.end()); - boundary.resize(e - boundary.begin()); - for(unsigned int i = 2; i < boundary.size(); i++) { - - } - if (boundary.size() < 4) { - return; - } - unsigned stac = 2; - for(unsigned int i = 2; i < boundary.size(); i++) { - double o = SignedTriangleArea(boundary[stac-2], - boundary[stac-1], - boundary[i]); - if(o == 0) { // colinear - dangerous... - stac--; - } else if(o < 0) { // anticlockwise - } else { // remove concavity - while(o >= 0 && stac > 2) { - stac--; - o = SignedTriangleArea(boundary[stac-2], - boundary[stac-1], - boundary[i]); - } - } - boundary[stac++] = boundary[i]; - } - boundary.resize(stac); -} - -// following code is from marco. - -/* - * return true in case the oriented polyline p0, p1, p2 is a right turn - */ -inline -bool is_a_right_turn (Point const& p0, Point const& p1, Point const& p2) -{ - if (p1 == p2) return false; - Point q1 = p1 - p0; - Point q2 = p2 - p0; - if (q1 == -q2) return false; - return (cross (q1, q2) < 0); -} - -/* - * return true if p < q wrt the lexicographyc order induced by the coordinates - */ -struct lex_less -{ - bool operator() (Point const& p, Point const& q) - { - return ((p[Y] < q[Y]) || (p[Y] == q[Y] && p[X] < q[X])); - } -}; - -/* - * return true if p > q wrt the lexicographyc order induced by the coordinates - */ -struct lex_greater -{ - bool operator() (Point const& p, Point const& q) - { - return ((p[Y] > q[Y]) || (p[Y] == q[Y] && p[X] > q[X])); - } -}; - -/* - * Compute the convex hull of a set of points. - * The implementation is based on the Andrew's scan algorithm - * note: in the Bezier clipping for collinear normals it seems - * to be more stable wrt the Graham's scan algorithm and in general - * a bit quikier - */ -void ConvexHull::andrew_scan () -{ - vector<Point> & P = boundary; - size_t n = P.size(); - if (n < 2) return; - std::sort(P.begin(), P.end(), lex_less()); - if (n < 4) return; - // upper hull - size_t u = 2; - for (size_t i = 2; i < n; ++i) - { - while (u > 1 && !is_a_right_turn(P[u-2], P[u-1], P[i])) - { - --u; - } - std::swap(P[u], P[i]); - ++u; - } - std::sort(P.begin() + u, P.end(), lex_greater()); - std::rotate(P.begin(), P.begin() + 1, P.end()); - // lower hull - size_t l = u; - size_t k = u - 1; - for (size_t i = l; i < n; ++i) - { - while (l > k && !is_a_right_turn(P[l-2], P[l-1], P[i])) - { - --l; - } - std::swap(P[l], P[i]); - ++l; - } - P.resize(l); -} - -void -ConvexHull::graham() { - /*if(is_degenerate()) // nothing to do - return;*/ - //find_pivot(); - //angle_sort(); - //graham_scan(); - andrew_scan(); -} - //Mathematically incorrect mod, but more useful. int mod(int i, int l) { return i >= 0 ? @@ -266,65 +358,6 @@ int mod(int i, int l) { } //OPT: usages can often be replaced by conditions -/*** ConvexHull::left - * Tests if a point is left (outside) of a particular segment, n. */ -bool -ConvexHull::is_left(Point p, int n) { - return SignedTriangleArea((*this)[n], (*this)[n+1], p) > 0; -} - -/*** ConvexHull::strict_left - * Tests if a point is left (outside) of a particular segment, n. */ -bool -ConvexHull::is_strict_left(Point p, int n) { - return SignedTriangleArea((*this)[n], (*this)[n+1], p) >= 0; -} - -/*** ConvexHull::find_left - * May return any number n where the segment n -> n + 1 (possibly looped around) in the hull such - * that the point is on the wrong side to be within the hull. Returns -1 if it is within the hull.*/ -int -ConvexHull::find_left(Point p) { - int l = boundary.size(); //Who knows if C++ is smart enough to optimize this? - for(int i = 0; i < l; i++) { - if(is_left(p, i)) return i; - } - return -1; -} - - -/*** ConvexHull::find_positive - * May return any number n where the segment n -> n + 1 (possibly looped around) in the hull such - * that the point is on the wrong side to be within the hull. Returns -1 if it is within the hull.*/ -int -ConvexHull::find_strict_left(Point p) { - int l = boundary.size(); //Who knows if C++ is smart enough to optimize this? - for(int i = 0; i < l; i++) { - if(is_strict_left(p, i)) return i; - } - return -1; -} - -//OPT: do a spread iteration - quasi-random with no repeats and full coverage. - -/*** ConvexHull::contains_point - * In order to test whether a point is inside a convex hull we can travel once around the outside making - * sure that each triangle made from an edge and the point has positive area. */ -bool -ConvexHull::contains_point(Point p) { - if(size() == 0) return false; - return find_left(p) == -1; -} - -/*** ConvexHull::strict_contains_point - * In order to test whether a point is strictly inside (not on the boundary) a convex hull we can travel once around the outside making - * sure that each triangle made from an edge and the point has positive area. */ -bool -ConvexHull::strict_contains_point(Point p) { - if(size() == 0) return false; - return find_strict_left(p) == -1; -} - /*** ConvexHull::add_point * to add a point we need to find whether the new point extends the boundary, and if so, what it * obscures. Tarjan? Jarvis?*/ @@ -332,18 +365,18 @@ void ConvexHull::merge(Point p) { std::vector<Point> out; - int l = boundary.size(); + int len = boundary.size(); - if(l < 2) { + if(len < 2) { if(boundary.empty() || boundary[0] != p) boundary.push_back(p); return; } bool pushed = false; - + bool pre = is_left(p, -1); - for(int i = 0; i < l; i++) { + for(int i = 0; i < len; i++) { bool cur = is_left(p, i); if(pre) { if(cur) { @@ -595,7 +628,7 @@ ConvexHull graham_merge(ConvexHull a, ConvexHull b) { // we can avoid the find pivot step because of top_point_first if(b.boundary[0] <= a.boundary[0]) - std::swap(a, b); + swap(a, b); result.boundary = a.boundary; result.boundary.insert(result.boundary.end(), @@ -615,7 +648,7 @@ ConvexHull andrew_merge(ConvexHull a, ConvexHull b) { // we can avoid the find pivot step because of top_point_first if(b.boundary[0] <= a.boundary[0]) - std::swap(a, b); + swap(a, b); result.boundary = a.boundary; result.boundary.insert(result.boundary.end(), @@ -697,6 +730,7 @@ double ConvexHull::narrowest_diameter(Point &a, Point &b, Point &c) { } return d; } +#endif }; diff --git a/src/2geom/convex-hull.h b/src/2geom/convex-hull.h new file mode 100644 index 000000000..4f4d10bd6 --- /dev/null +++ b/src/2geom/convex-hull.h @@ -0,0 +1,346 @@ +/** @file + * @brief Convex hull data structures + *//* + * Copyright 2006 Nathan Hurst <njh@mail.csse.monash.edu.au> + * Copyright 2006 Michael G. Sloan <mgsloan@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + */ + +#ifndef LIB2GEOM_SEEN_CONVEX_HULL_H +#define LIB2GEOM_SEEN_CONVEX_HULL_H + +#include <2geom/point.h> +#include <2geom/rect.h> +#include <vector> +#include <algorithm> +#include <boost/operators.hpp> +#include <boost/optional.hpp> +#include <boost/range/iterator_range.hpp> + +namespace Geom { + +namespace { + +/** @brief Iterator for the lower convex hull. + * This iterator allows us to avoid duplicating any points in the hull + * boundary and still express most algorithms in a concise way. */ +class ConvexHullLowerIterator + : public boost::random_access_iterator_helper + < ConvexHullLowerIterator + , Point + , std::ptrdiff_t + , Point const * + , Point const & + > +{ +public: + typedef ConvexHullLowerIterator Self; + ConvexHullLowerIterator() + : _data(NULL) + , _size(0) + , _x(0) + {} + ConvexHullLowerIterator(std::vector<Point> const &pts, std::size_t x) + : _data(&pts[0]) + , _size(pts.size()) + , _x(x) + {} + + Self &operator++() { + *this += 1; + return *this; + } + Self &operator--() { + *this -= 1; + return *this; + } + Self &operator+=(std::ptrdiff_t d) { + _x += d; + return *this; + } + Self &operator-=(std::ptrdiff_t d) { + _x -= d; + return *this; + } + std::ptrdiff_t operator-(Self const &other) const { + return _x - other._x; + } + Point const &operator*() const { + if (_x < _size) { + return _data[_x]; + } else { + return *_data; + } + } + bool operator==(Self const &other) const { + return _data == other._data && _x == other._x; + } + bool operator<(Self const &other) const { + return _data == other._data && _x < other._x; + } + +private: + Point const *_data; + std::size_t _size; + std::size_t _x; +}; + +} // end anonymous namespace + +/** + * @brief Convex hull based on the Andrew's monotone chain algorithm. + * @ingroup Shapes + */ +class ConvexHull { +public: + typedef std::vector<Point>::const_iterator iterator; + typedef std::vector<Point>::const_iterator const_iterator; + typedef std::vector<Point>::const_iterator UpperIterator; + typedef ConvexHullLowerIterator LowerIterator; + + /// @name Construct a convex hull. + /// @{ + + /// Create an empty convex hull. + ConvexHull() {} + /// Construct a singular convex hull. + explicit ConvexHull(Point const &a) + : _boundary(1, a) + , _lower(1) + {} + /// Construct a convex hull of two points. + ConvexHull(Point const &a, Point const &b); + /// Construct a convex hull of three points. + ConvexHull(Point const &a, Point const &b, Point const &c); + /// Construct a convex hull of four points. + ConvexHull(Point const &a, Point const &b, Point const &c, Point const &d); + /// Create a convex hull of a vector of points. + ConvexHull(std::vector<Point> const &pts); + + /// Create a convex hull of a range of points. + template <typename Iter> + ConvexHull(Iter first, Iter last) + : _lower(0) + { + _prune(first, last, _boundary); + _construct(); + } + /// @} + + /// @name Inspect basic properties. + /// @{ + + /// Check for emptiness. + bool empty() const { return _boundary.empty(); } + /// Get the number of points in the hull. + size_t size() const { return _boundary.size(); } + /// Check whether the hull contains only one point. + bool isSingular() const { return _boundary.size() == 1; } + /// Check whether the hull is a line. + bool isLinear() const { return _boundary.size() == 2; } + /// Check whether the hull has zero area. + bool isDegenerate() const { return _boundary.size() < 3; } + /// Calculate the area of the convex hull. + double area() const; + //Point centroid() const; + //double areaAndCentroid(Point &c); + //FatLine maxDiameter() const; + //FatLine minDiameter() const; + /// @} + + /// @name Inspect bounds and extreme points. + /// @{ + + /// Compute the bounding rectangle of the convex hull. + OptRect bounds() const; + + /// Get the leftmost (minimum X) coordinate of the hull. + Coord left() const { return _boundary[0][X]; } + /// Get the rightmost (maximum X) coordinate of the hull. + Coord right() const { return _boundary[_lower-1][X]; } + /// Get the topmost (minimum Y) coordinate of the hull. + Coord top() const { return topPoint()[Y]; } + /// Get the bottommost (maximum Y) coordinate of the hull. + Coord bottom() const { return bottomPoint()[Y]; } + + /// Get the leftmost (minimum X) point of the hull. + /// If the leftmost edge is vertical, the top point of the edge is returned. + Point leftPoint() const { return _boundary[0]; } + /// Get the rightmost (maximum X) point of the hull. + /// If the rightmost edge is vertical, the bottom point edge is returned. + Point rightPoint() const { return _boundary[_lower-1]; } + /// Get the topmost (minimum Y) point of the hull. + /// If the topmost edge is horizontal, the right point of the edge is returned. + Point topPoint() const; + /// Get the bottommost (maximum Y) point of the hull. + /// If the bottommost edge is horizontal, the left point of the edge is returned. + Point bottomPoint() const; + ///@} + + /// @name Iterate over points. + /// @{ + /** @brief Get the begin iterator to the points that form the hull. + * Points are are returned beginning the the leftmost one, going along + * the upper (minimum Y) side, and then along the bottom. + * Thus the points are always ordered clockwise. No point is + * repeated. */ + iterator begin() const { return _boundary.begin(); } + /// Get the end iterator to the points that form the hull. + iterator end() const { return _boundary.end(); } + /// Get the first, leftmost point in the hull. + Point const &front() const { return _boundary.front(); } + /// Get the penultimate point of the lower hull. + Point const &back() const { return _boundary.back(); } + Point const &operator[](std::size_t i) const { + return _boundary[i]; + } + + /** @brief Get an iterator range to the upper part of the hull. + * This returns a range that includes the leftmost point, + * all points of the upper hull, and the rightmost point. */ + boost::iterator_range<UpperIterator> upperHull() const { + boost::iterator_range<UpperIterator> r(_boundary.begin(), _boundary.begin() + _lower); + return r; + } + + /** @brief Get an iterator range to the lower part of the hull. + * This returns a range that includes the leftmost point, + * all points of the lower hull, and the rightmost point. */ + boost::iterator_range<LowerIterator> lowerHull() const { + if (_boundary.empty()) { + boost::iterator_range<LowerIterator> r(LowerIterator(_boundary, 0), + LowerIterator(_boundary, 0)); + return r; + } + if (_boundary.size() == 1) { + boost::iterator_range<LowerIterator> r(LowerIterator(_boundary, 0), + LowerIterator(_boundary, 1)); + return r; + } + boost::iterator_range<LowerIterator> r(LowerIterator(_boundary, _lower - 1), + LowerIterator(_boundary, _boundary.size() + 1)); + return r; + } + /// @} + + /// @name Check for containment and intersection. + /// @{ + /** @brief Check whether the given point is inside the hull. + * This takes logarithmic time. */ + bool contains(Point const &p) const; + /** @brief Check whether the given axis-aligned rectangle is inside the hull. + * A rectangle is inside the hull if all of its corners are inside. */ + bool contains(Rect const &r) const; + /// Check whether the given convex hull is completely contained in this one. + bool contains(ConvexHull const &other) const; + //bool interiorContains(Point const &p) const; + //bool interiorContains(Rect const &r) const; + //bool interiorContains(ConvexHull const &other) const; + //bool intersects(Rect const &r) const; + //bool intersects(ConvexHull const &other) const; + + //ConvexHull &operator|=(ConvexHull const &other); + //ConvexHull &operator&=(ConvexHull const &other); + //ConvexHull &operator*=(Affine const &m); + + //ConvexHull &expand(Point const &p); + //void unifyWith(ConvexHull const &other); + //void intersectWith(ConvexHull const &other); + /// @} + + void swap(ConvexHull &other); + void swap(std::vector<Point> &pts); + +private: + void _construct(); + static bool _is_clockwise_turn(Point const &a, Point const &b, Point const &c); + + /// Take a vector of points and produce a pruned sorted vector. + template <typename Iter> + static void _prune(Iter first, Iter last, std::vector<Point> &out) { + boost::optional<Point> ymin, ymax, xmin, xmax; + for (Iter i = first; i != last; ++i) { + Point p = *i; + if (!ymin || Point::LexLess<Y>()(p, *ymin)) { + ymin = p; + } + if (!xmin || Point::LexLess<X>()(p, *xmin)) { + xmin = p; + } + if (!ymax || Point::LexGreater<Y>()(p, *ymax)) { + ymax = p; + } + if (!xmax || Point::LexGreater<X>()(p, *xmax)) { + xmax = p; + } + } + if (!ymin) return; + + ConvexHull qhull(*xmin, *xmax, *ymin, *ymax); + for (Iter i = first; i != last; ++i) { + if (qhull.contains(*i)) continue; + out.push_back(*i); + } + + out.push_back(*xmin); + out.push_back(*xmax); + out.push_back(*ymin); + out.push_back(*ymax); + std::sort(out.begin(), out.end(), Point::LexLess<X>()); + out.erase(std::unique(out.begin(), out.end()), out.end()); + } + + /// Sequence of points forming the convex hull polygon. + std::vector<Point> _boundary; + /// Index one past the rightmost point, where the lower part of the boundary starts. + std::size_t _lower; +}; + +/** @brief Output operator for convex hulls. + * Prints out all the coordinates. */ +inline std::ostream &operator<< (std::ostream &out_file, const Geom::ConvexHull &in_cvx) { + out_file << "ConvexHull("; + for(unsigned i = 0; i < in_cvx.size(); i++) { + out_file << in_cvx[i] << ", "; + } + out_file << ")"; + return out_file; +} + +} // end namespace Geom + +#endif // LIB2GEOM_SEEN_CONVEX_HULL_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/2geom/coord.cpp b/src/2geom/coord.cpp new file mode 100644 index 000000000..8b5e28586 --- /dev/null +++ b/src/2geom/coord.cpp @@ -0,0 +1,3688 @@ +/** @file + * @brief Conversion between Coord and strings + *//* + * Authors: + * Krzysztof Kosiński <tweenk.pl@gmail.com> + * + * Copyright 2014 Authors + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + */ + +// Most of the code in this file is derived from: +// https://code.google.com/p/double-conversion/ +// The copyright notice for that code is attached below. +// +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <2geom/coord.h> +#include <stdint.h> +#include <cstdlib> +#include <cassert> +#include <cstring> +#include <climits> +#include <cstdarg> +#include <cmath> + +#ifndef ASSERT +#define ASSERT(condition) \ + assert(condition); +#endif +#ifndef UNIMPLEMENTED +#define UNIMPLEMENTED() (abort()) +#endif +#ifndef UNREACHABLE +#define UNREACHABLE() (abort()) +#endif + +#define UINT64_2PART_C(a, b) (((static_cast<uint64_t>(a) << 32) + 0x##b##u)) + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(a) \ + ((sizeof(a) / sizeof(*(a))) / \ + static_cast<size_t>(!(sizeof(a) % sizeof(*(a))))) +#endif + +#ifndef DISALLOW_COPY_AND_ASSIGN +#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ + TypeName(const TypeName&); \ + void operator=(const TypeName&) +#endif + +#ifndef DISALLOW_IMPLICIT_CONSTRUCTORS +#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \ + TypeName(); \ + DISALLOW_COPY_AND_ASSIGN(TypeName) +#endif + +#if defined(__GNUC__) +#define DOUBLE_CONVERSION_UNUSED __attribute__((unused)) +#else +#define DOUBLE_CONVERSION_UNUSED +#endif + +namespace Geom { + +namespace { + +inline int StrLength(const char* string) { + size_t length = strlen(string); + ASSERT(length == static_cast<size_t>(static_cast<int>(length))); + return static_cast<int>(length); +} + +template <typename T> +class Vector { + public: + Vector() : start_(NULL), length_(0) {} + Vector(T* data, int length) : start_(data), length_(length) { + ASSERT(length == 0 || (length > 0 && data != NULL)); + } + + Vector<T> SubVector(int from, int to) { + ASSERT(to <= length_); + ASSERT(from < to); + ASSERT(0 <= from); + return Vector<T>(start() + from, to - from); + } + int length() const { return length_; } + bool is_empty() const { return length_ == 0; } + + T* start() const { return start_; } + + T& operator[](int index) const { + ASSERT(0 <= index && index < length_); + return start_[index]; + } + T& first() { return start_[0]; } + T& last() { return start_[length_ - 1]; } + + private: + T* start_; + int length_; +}; + +template <class Dest, class Source> +inline Dest BitCast(const Source& source) { + DOUBLE_CONVERSION_UNUSED + typedef char VerifySizesAreEqual[sizeof(Dest) == sizeof(Source) ? 1 : -1]; + Dest dest; + memmove(&dest, &source, sizeof(dest)); + return dest; +} + +template <class Dest, class Source> +inline Dest BitCast(Source* source) { + return BitCast<Dest>(reinterpret_cast<uintptr_t>(source)); +} + +// We assume that doubles and uint64_t have the same endianness. +static uint64_t double_to_uint64(double d) { return BitCast<uint64_t>(d); } +static double uint64_to_double(uint64_t d64) { return BitCast<double>(d64); } + +// This "Do It Yourself Floating Point" class +class DiyFp { + public: + static const int kSignificandSize = 64; + + DiyFp() : f_(0), e_(0) {} + DiyFp(uint64_t f, int e) : f_(f), e_(e) {} + + void Subtract(const DiyFp& other) { + ASSERT(e_ == other.e_); + ASSERT(f_ >= other.f_); + f_ -= other.f_; + } + + static DiyFp Minus(const DiyFp& a, const DiyFp& b) { + DiyFp result = a; + result.Subtract(b); + return result; + } + + void Multiply(const DiyFp& other) { + const uint64_t kM32 = 0xFFFFFFFFU; + uint64_t a = f_ >> 32; + uint64_t b = f_ & kM32; + uint64_t c = other.f_ >> 32; + uint64_t d = other.f_ & kM32; + uint64_t ac = a * c; + uint64_t bc = b * c; + uint64_t ad = a * d; + uint64_t bd = b * d; + uint64_t tmp = (bd >> 32) + (ad & kM32) + (bc & kM32); + // By adding 1U << 31 to tmp we round the final result. + // Halfway cases will be round up. + tmp += 1U << 31; + uint64_t result_f = ac + (ad >> 32) + (bc >> 32) + (tmp >> 32); + e_ += other.e_ + 64; + f_ = result_f; + } + + static DiyFp Times(const DiyFp& a, const DiyFp& b) { + DiyFp result = a; + result.Multiply(b); + return result; + } + + void Normalize() { + ASSERT(f_ != 0); + uint64_t f = f_; + int e = e_; + + const uint64_t k10MSBits = UINT64_2PART_C(0xFFC00000, 00000000); + while ((f & k10MSBits) == 0) { + f <<= 10; + e -= 10; + } + while ((f & kUint64MSB) == 0) { + f <<= 1; + e--; + } + f_ = f; + e_ = e; + } + + static DiyFp Normalize(const DiyFp& a) { + DiyFp result = a; + result.Normalize(); + return result; + } + + uint64_t f() const { return f_; } + int e() const { return e_; } + + void set_f(uint64_t new_value) { f_ = new_value; } + void set_e(int new_value) { e_ = new_value; } + + private: + static const uint64_t kUint64MSB = UINT64_2PART_C(0x80000000, 00000000); + + uint64_t f_; + int e_; +}; + +class Double { + public: + static const uint64_t kSignMask = UINT64_2PART_C(0x80000000, 00000000); + static const uint64_t kExponentMask = UINT64_2PART_C(0x7FF00000, 00000000); + static const uint64_t kSignificandMask = UINT64_2PART_C(0x000FFFFF, FFFFFFFF); + static const uint64_t kHiddenBit = UINT64_2PART_C(0x00100000, 00000000); + static const int kPhysicalSignificandSize = 52; // Excludes the hidden bit. + static const int kSignificandSize = 53; + + Double() : d64_(0) {} + explicit Double(double d) : d64_(double_to_uint64(d)) {} + explicit Double(uint64_t d64) : d64_(d64) {} + explicit Double(DiyFp diy_fp) + : d64_(DiyFpToUint64(diy_fp)) {} + + DiyFp AsDiyFp() const { + ASSERT(Sign() > 0); + ASSERT(!IsSpecial()); + return DiyFp(Significand(), Exponent()); + } + + DiyFp AsNormalizedDiyFp() const { + ASSERT(value() > 0.0); + uint64_t f = Significand(); + int e = Exponent(); + + // The current double could be a denormal. + while ((f & kHiddenBit) == 0) { + f <<= 1; + e--; + } + // Do the final shifts in one go. + f <<= DiyFp::kSignificandSize - kSignificandSize; + e -= DiyFp::kSignificandSize - kSignificandSize; + return DiyFp(f, e); + } + + uint64_t AsUint64() const { + return d64_; + } + + double NextDouble() const { + if (d64_ == kInfinity) return Double(kInfinity).value(); + if (Sign() < 0 && Significand() == 0) { + // -0.0 + return 0.0; + } + if (Sign() < 0) { + return Double(d64_ - 1).value(); + } else { + return Double(d64_ + 1).value(); + } + } + + double PreviousDouble() const { + if (d64_ == (kInfinity | kSignMask)) return -Double::Infinity(); + if (Sign() < 0) { + return Double(d64_ + 1).value(); + } else { + if (Significand() == 0) return -0.0; + return Double(d64_ - 1).value(); + } + } + + int Exponent() const { + if (IsDenormal()) return kDenormalExponent; + + uint64_t d64 = AsUint64(); + int biased_e = + static_cast<int>((d64 & kExponentMask) >> kPhysicalSignificandSize); + return biased_e - kExponentBias; + } + + uint64_t Significand() const { + uint64_t d64 = AsUint64(); + uint64_t significand = d64 & kSignificandMask; + if (!IsDenormal()) { + return significand + kHiddenBit; + } else { + return significand; + } + } + + bool IsDenormal() const { + uint64_t d64 = AsUint64(); + return (d64 & kExponentMask) == 0; + } + + // We consider denormals not to be special. + // Hence only Infinity and NaN are special. + bool IsSpecial() const { + uint64_t d64 = AsUint64(); + return (d64 & kExponentMask) == kExponentMask; + } + + bool IsNan() const { + uint64_t d64 = AsUint64(); + return ((d64 & kExponentMask) == kExponentMask) && + ((d64 & kSignificandMask) != 0); + } + + bool IsInfinite() const { + uint64_t d64 = AsUint64(); + return ((d64 & kExponentMask) == kExponentMask) && + ((d64 & kSignificandMask) == 0); + } + + int Sign() const { + uint64_t d64 = AsUint64(); + return (d64 & kSignMask) == 0? 1: -1; + } + + DiyFp UpperBoundary() const { + ASSERT(Sign() > 0); + return DiyFp(Significand() * 2 + 1, Exponent() - 1); + } + + void NormalizedBoundaries(DiyFp* out_m_minus, DiyFp* out_m_plus) const { + ASSERT(value() > 0.0); + DiyFp v = this->AsDiyFp(); + DiyFp m_plus = DiyFp::Normalize(DiyFp((v.f() << 1) + 1, v.e() - 1)); + DiyFp m_minus; + if (LowerBoundaryIsCloser()) { + m_minus = DiyFp((v.f() << 2) - 1, v.e() - 2); + } else { + m_minus = DiyFp((v.f() << 1) - 1, v.e() - 1); + } + m_minus.set_f(m_minus.f() << (m_minus.e() - m_plus.e())); + m_minus.set_e(m_plus.e()); + *out_m_plus = m_plus; + *out_m_minus = m_minus; + } + + bool LowerBoundaryIsCloser() const { + bool physical_significand_is_zero = ((AsUint64() & kSignificandMask) == 0); + return physical_significand_is_zero && (Exponent() != kDenormalExponent); + } + + double value() const { return uint64_to_double(d64_); } + + static int SignificandSizeForOrderOfMagnitude(int order) { + if (order >= (kDenormalExponent + kSignificandSize)) { + return kSignificandSize; + } + if (order <= kDenormalExponent) return 0; + return order - kDenormalExponent; + } + + static double Infinity() { + return Double(kInfinity).value(); + } + + static double NaN() { + return Double(kNaN).value(); + } + + private: + static const int kExponentBias = 0x3FF + kPhysicalSignificandSize; + static const int kDenormalExponent = -kExponentBias + 1; + static const int kMaxExponent = 0x7FF - kExponentBias; + static const uint64_t kInfinity = UINT64_2PART_C(0x7FF00000, 00000000); + static const uint64_t kNaN = UINT64_2PART_C(0x7FF80000, 00000000); + + const uint64_t d64_; + + static uint64_t DiyFpToUint64(DiyFp diy_fp) { + uint64_t significand = diy_fp.f(); + int exponent = diy_fp.e(); + while (significand > kHiddenBit + kSignificandMask) { + significand >>= 1; + exponent++; + } + if (exponent >= kMaxExponent) { + return kInfinity; + } + if (exponent < kDenormalExponent) { + return 0; + } + while (exponent > kDenormalExponent && (significand & kHiddenBit) == 0) { + significand <<= 1; + exponent--; + } + uint64_t biased_exponent; + if (exponent == kDenormalExponent && (significand & kHiddenBit) == 0) { + biased_exponent = 0; + } else { + biased_exponent = static_cast<uint64_t>(exponent + kExponentBias); + } + return (significand & kSignificandMask) | + (biased_exponent << kPhysicalSignificandSize); + } + + DISALLOW_COPY_AND_ASSIGN(Double); +}; + +template<typename S> +static int BitSize(S value) { + (void) value; // Mark variable as used. + return 8 * sizeof(value); +} + +class Bignum { + public: + // 3584 = 128 * 28. We can represent 2^3584 > 10^1000 accurately. + // This bignum can encode much bigger numbers, since it contains an + // exponent. + static const int kMaxSignificantBits = 3584; + + Bignum() + : bigits_(bigits_buffer_, kBigitCapacity), used_digits_(0), exponent_(0) + { + for (int i = 0; i < kBigitCapacity; ++i) { + bigits_[i] = 0; + } + } + void AssignUInt16(uint16_t value) { + ASSERT(kBigitSize >= BitSize(value)); + Zero(); + if (value == 0) return; + + EnsureCapacity(1); + bigits_[0] = value; + used_digits_ = 1; + } + void AssignUInt64(uint64_t value) { + const int kUInt64Size = 64; + + Zero(); + if (value == 0) return; + + int needed_bigits = kUInt64Size / kBigitSize + 1; + EnsureCapacity(needed_bigits); + for (int i = 0; i < needed_bigits; ++i) { + bigits_[i] = value & kBigitMask; + value = value >> kBigitSize; + } + used_digits_ = needed_bigits; + Clamp(); + } + void AssignBignum(const Bignum& other) { + exponent_ = other.exponent_; + for (int i = 0; i < other.used_digits_; ++i) { + bigits_[i] = other.bigits_[i]; + } + // Clear the excess digits (if there were any). + for (int i = other.used_digits_; i < used_digits_; ++i) { + bigits_[i] = 0; + } + used_digits_ = other.used_digits_; + } + + void AssignDecimalString(Vector<const char> value); + void AssignHexString(Vector<const char> value); + + void AssignPowerUInt16(uint16_t base, int exponent); + + void AddUInt16(uint16_t operand); + void AddUInt64(uint64_t operand); + void AddBignum(const Bignum& other); + // Precondition: this >= other. + void SubtractBignum(const Bignum& other); + + void Square(); + void ShiftLeft(int shift_amount); + void MultiplyByUInt32(uint32_t factor); + void MultiplyByUInt64(uint64_t factor); + void MultiplyByPowerOfTen(int exponent); + void Times10() { return MultiplyByUInt32(10); } + // Pseudocode: + // int result = this / other; + // this = this % other; + // In the worst case this function is in O(this/other). + uint16_t DivideModuloIntBignum(const Bignum& other); + + bool ToHexString(char* buffer, int buffer_size) const; + + // Returns + // -1 if a < b, + // 0 if a == b, and + // +1 if a > b. + static int Compare(const Bignum& a, const Bignum& b); + static bool Equal(const Bignum& a, const Bignum& b) { + return Compare(a, b) == 0; + } + static bool LessEqual(const Bignum& a, const Bignum& b) { + return Compare(a, b) <= 0; + } + static bool Less(const Bignum& a, const Bignum& b) { + return Compare(a, b) < 0; + } + // Returns Compare(a + b, c); + static int PlusCompare(const Bignum& a, const Bignum& b, const Bignum& c); + // Returns a + b == c + static bool PlusEqual(const Bignum& a, const Bignum& b, const Bignum& c) { + return PlusCompare(a, b, c) == 0; + } + // Returns a + b <= c + static bool PlusLessEqual(const Bignum& a, const Bignum& b, const Bignum& c) { + return PlusCompare(a, b, c) <= 0; + } + // Returns a + b < c + static bool PlusLess(const Bignum& a, const Bignum& b, const Bignum& c) { + return PlusCompare(a, b, c) < 0; + } + private: + typedef uint32_t Chunk; + typedef uint64_t DoubleChunk; + + static const int kChunkSize = sizeof(Chunk) * 8; + static const int kDoubleChunkSize = sizeof(DoubleChunk) * 8; + // With bigit size of 28 we loose some bits, but a double still fits easily + // into two chunks, and more importantly we can use the Comba multiplication. + static const int kBigitSize = 28; + static const Chunk kBigitMask = (1 << kBigitSize) - 1; + // Every instance allocates kBigitLength chunks on the stack. Bignums cannot + // grow. There are no checks if the stack-allocated space is sufficient. + static const int kBigitCapacity = kMaxSignificantBits / kBigitSize; + + void EnsureCapacity(int size) { + if (size > kBigitCapacity) { + UNREACHABLE(); + } + } + void Align(const Bignum& other); + void Clamp(); + bool IsClamped() const; + void Zero(); + // Requires this to have enough capacity (no tests done). + // Updates used_digits_ if necessary. + // shift_amount must be < kBigitSize. + void BigitsShiftLeft(int shift_amount); + // BigitLength includes the "hidden" digits encoded in the exponent. + int BigitLength() const { return used_digits_ + exponent_; } + Chunk BigitAt(int index) const; + void SubtractTimes(const Bignum& other, int factor); + + Chunk bigits_buffer_[kBigitCapacity]; + // A vector backed by bigits_buffer_. This way accesses to the array are + // checked for out-of-bounds errors. + Vector<Chunk> bigits_; + int used_digits_; + // The Bignum's value equals value(bigits_) * 2^(exponent_ * kBigitSize). + int exponent_; + + DISALLOW_COPY_AND_ASSIGN(Bignum); +}; + +static uint64_t ReadUInt64(Vector<const char> buffer, + int from, + int digits_to_read) { + uint64_t result = 0; + for (int i = from; i < from + digits_to_read; ++i) { + int digit = buffer[i] - '0'; + ASSERT(0 <= digit && digit <= 9); + result = result * 10 + digit; + } + return result; +} + + +void Bignum::AssignDecimalString(Vector<const char> value) { + // 2^64 = 18446744073709551616 > 10^19 + const int kMaxUint64DecimalDigits = 19; + Zero(); + int length = value.length(); + int pos = 0; + // Let's just say that each digit needs 4 bits. + while (length >= kMaxUint64DecimalDigits) { + uint64_t digits = ReadUInt64(value, pos, kMaxUint64DecimalDigits); + pos += kMaxUint64DecimalDigits; + length -= kMaxUint64DecimalDigits; + MultiplyByPowerOfTen(kMaxUint64DecimalDigits); + AddUInt64(digits); + } + uint64_t digits = ReadUInt64(value, pos, length); + MultiplyByPowerOfTen(length); + AddUInt64(digits); + Clamp(); +} + + +static int HexCharValue(char c) { + if ('0' <= c && c <= '9') return c - '0'; + if ('a' <= c && c <= 'f') return 10 + c - 'a'; + ASSERT('A' <= c && c <= 'F'); + return 10 + c - 'A'; +} + + +void Bignum::AssignHexString(Vector<const char> value) { + Zero(); + int length = value.length(); + + int needed_bigits = length * 4 / kBigitSize + 1; + EnsureCapacity(needed_bigits); + int string_index = length - 1; + for (int i = 0; i < needed_bigits - 1; ++i) { + // These bigits are guaranteed to be "full". + Chunk current_bigit = 0; + for (int j = 0; j < kBigitSize / 4; j++) { + current_bigit += HexCharValue(value[string_index--]) << (j * 4); + } + bigits_[i] = current_bigit; + } + used_digits_ = needed_bigits - 1; + + Chunk most_significant_bigit = 0; // Could be = 0; + for (int j = 0; j <= string_index; ++j) { + most_significant_bigit <<= 4; + most_significant_bigit += HexCharValue(value[j]); + } + if (most_significant_bigit != 0) { + bigits_[used_digits_] = most_significant_bigit; + used_digits_++; + } + Clamp(); +} + + +void Bignum::AddUInt64(uint64_t operand) { + if (operand == 0) return; + Bignum other; + other.AssignUInt64(operand); + AddBignum(other); +} + + +void Bignum::AddBignum(const Bignum& other) { + ASSERT(IsClamped()); + ASSERT(other.IsClamped()); + + Align(other); + + EnsureCapacity(1 + std::max(BigitLength(), other.BigitLength()) - exponent_); + Chunk carry = 0; + int bigit_pos = other.exponent_ - exponent_; + ASSERT(bigit_pos >= 0); + for (int i = 0; i < other.used_digits_; ++i) { + Chunk sum = bigits_[bigit_pos] + other.bigits_[i] + carry; + bigits_[bigit_pos] = sum & kBigitMask; + carry = sum >> kBigitSize; + bigit_pos++; + } + + while (carry != 0) { + Chunk sum = bigits_[bigit_pos] + carry; + bigits_[bigit_pos] = sum & kBigitMask; + carry = sum >> kBigitSize; + bigit_pos++; + } + used_digits_ = std::max(bigit_pos, used_digits_); + ASSERT(IsClamped()); +} + + +void Bignum::SubtractBignum(const Bignum& other) { + ASSERT(IsClamped()); + ASSERT(other.IsClamped()); + // We require this to be bigger than other. + ASSERT(LessEqual(other, *this)); + + Align(other); + + int offset = other.exponent_ - exponent_; + Chunk borrow = 0; + int i; + for (i = 0; i < other.used_digits_; ++i) { + ASSERT((borrow == 0) || (borrow == 1)); + Chunk difference = bigits_[i + offset] - other.bigits_[i] - borrow; + bigits_[i + offset] = difference & kBigitMask; + borrow = difference >> (kChunkSize - 1); + } + while (borrow != 0) { + Chunk difference = bigits_[i + offset] - borrow; + bigits_[i + offset] = difference & kBigitMask; + borrow = difference >> (kChunkSize - 1); + ++i; + } + Clamp(); +} + + +void Bignum::ShiftLeft(int shift_amount) { + if (used_digits_ == 0) return; + exponent_ += shift_amount / kBigitSize; + int local_shift = shift_amount % kBigitSize; + EnsureCapacity(used_digits_ + 1); + BigitsShiftLeft(local_shift); +} + + +void Bignum::MultiplyByUInt32(uint32_t factor) { + if (factor == 1) return; + if (factor == 0) { + Zero(); + return; + } + if (used_digits_ == 0) return; + + ASSERT(kDoubleChunkSize >= kBigitSize + 32 + 1); + DoubleChunk carry = 0; + for (int i = 0; i < used_digits_; ++i) { + DoubleChunk product = static_cast<DoubleChunk>(factor) * bigits_[i] + carry; + bigits_[i] = static_cast<Chunk>(product & kBigitMask); + carry = (product >> kBigitSize); + } + while (carry != 0) { + EnsureCapacity(used_digits_ + 1); + bigits_[used_digits_] = carry & kBigitMask; + used_digits_++; + carry >>= kBigitSize; + } +} + + +void Bignum::MultiplyByUInt64(uint64_t factor) { + if (factor == 1) return; + if (factor == 0) { + Zero(); + return; + } + ASSERT(kBigitSize < 32); + uint64_t carry = 0; + uint64_t low = factor & 0xFFFFFFFF; + uint64_t high = factor >> 32; + for (int i = 0; i < used_digits_; ++i) { + uint64_t product_low = low * bigits_[i]; + uint64_t product_high = high * bigits_[i]; + uint64_t tmp = (carry & kBigitMask) + product_low; + bigits_[i] = tmp & kBigitMask; + carry = (carry >> kBigitSize) + (tmp >> kBigitSize) + + (product_high << (32 - kBigitSize)); + } + while (carry != 0) { + EnsureCapacity(used_digits_ + 1); + bigits_[used_digits_] = carry & kBigitMask; + used_digits_++; + carry >>= kBigitSize; + } +} + + +void Bignum::MultiplyByPowerOfTen(int exponent) { + const uint64_t kFive27 = UINT64_2PART_C(0x6765c793, fa10079d); + const uint16_t kFive1 = 5; + const uint16_t kFive2 = kFive1 * 5; + const uint16_t kFive3 = kFive2 * 5; + const uint16_t kFive4 = kFive3 * 5; + const uint16_t kFive5 = kFive4 * 5; + const uint16_t kFive6 = kFive5 * 5; + const uint32_t kFive7 = kFive6 * 5; + const uint32_t kFive8 = kFive7 * 5; + const uint32_t kFive9 = kFive8 * 5; + const uint32_t kFive10 = kFive9 * 5; + const uint32_t kFive11 = kFive10 * 5; + const uint32_t kFive12 = kFive11 * 5; + const uint32_t kFive13 = kFive12 * 5; + const uint32_t kFive1_to_12[] = + { kFive1, kFive2, kFive3, kFive4, kFive5, kFive6, + kFive7, kFive8, kFive9, kFive10, kFive11, kFive12 }; + + ASSERT(exponent >= 0); + if (exponent == 0) return; + if (used_digits_ == 0) return; + + int remaining_exponent = exponent; + while (remaining_exponent >= 27) { + MultiplyByUInt64(kFive27); + remaining_exponent -= 27; + } + while (remaining_exponent >= 13) { + MultiplyByUInt32(kFive13); + remaining_exponent -= 13; + } + if (remaining_exponent > 0) { + MultiplyByUInt32(kFive1_to_12[remaining_exponent - 1]); + } + ShiftLeft(exponent); +} + + +void Bignum::Square() { + ASSERT(IsClamped()); + int product_length = 2 * used_digits_; + EnsureCapacity(product_length); + + if ((1 << (2 * (kChunkSize - kBigitSize))) <= used_digits_) { + UNIMPLEMENTED(); + } + DoubleChunk accumulator = 0; + // First shift the digits so we don't overwrite them. + int copy_offset = used_digits_; + for (int i = 0; i < used_digits_; ++i) { + bigits_[copy_offset + i] = bigits_[i]; + } + // We have two loops to avoid some 'if's in the loop. + for (int i = 0; i < used_digits_; ++i) { + // Process temporary digit i with power i. + // The sum of the two indices must be equal to i. + int bigit_index1 = i; + int bigit_index2 = 0; + // Sum all of the sub-products. + while (bigit_index1 >= 0) { + Chunk chunk1 = bigits_[copy_offset + bigit_index1]; + Chunk chunk2 = bigits_[copy_offset + bigit_index2]; + accumulator += static_cast<DoubleChunk>(chunk1) * chunk2; + bigit_index1--; + bigit_index2++; + } + bigits_[i] = static_cast<Chunk>(accumulator) & kBigitMask; + accumulator >>= kBigitSize; + } + for (int i = used_digits_; i < product_length; ++i) { + int bigit_index1 = used_digits_ - 1; + int bigit_index2 = i - bigit_index1; + + while (bigit_index2 < used_digits_) { + Chunk chunk1 = bigits_[copy_offset + bigit_index1]; + Chunk chunk2 = bigits_[copy_offset + bigit_index2]; + accumulator += static_cast<DoubleChunk>(chunk1) * chunk2; + bigit_index1--; + bigit_index2++; + } + bigits_[i] = static_cast<Chunk>(accumulator) & kBigitMask; + accumulator >>= kBigitSize; + } + + ASSERT(accumulator == 0); + + used_digits_ = product_length; + exponent_ *= 2; + Clamp(); +} + + +void Bignum::AssignPowerUInt16(uint16_t base, int power_exponent) { + ASSERT(base != 0); + ASSERT(power_exponent >= 0); + if (power_exponent == 0) { + AssignUInt16(1); + return; + } + Zero(); + int shifts = 0; + + while ((base & 1) == 0) { + base >>= 1; + shifts++; + } + int bit_size = 0; + int tmp_base = base; + while (tmp_base != 0) { + tmp_base >>= 1; + bit_size++; + } + int final_size = bit_size * power_exponent; + + EnsureCapacity(final_size / kBigitSize + 2); + + // Left to Right exponentiation. + int mask = 1; + while (power_exponent >= mask) mask <<= 1; + + mask >>= 2; + uint64_t this_value = base; + + bool delayed_multipliciation = false; + const uint64_t max_32bits = 0xFFFFFFFF; + while (mask != 0 && this_value <= max_32bits) { + this_value = this_value * this_value; + // Verify that there is enough space in this_value to perform the + // multiplication. The first bit_size bits must be 0. + if ((power_exponent & mask) != 0) { + uint64_t base_bits_mask = + ~((static_cast<uint64_t>(1) << (64 - bit_size)) - 1); + bool high_bits_zero = (this_value & base_bits_mask) == 0; + if (high_bits_zero) { + this_value *= base; + } else { + delayed_multipliciation = true; + } + } + mask >>= 1; + } + AssignUInt64(this_value); + if (delayed_multipliciation) { + MultiplyByUInt32(base); + } + + // Now do the same thing as a bignum. + while (mask != 0) { + Square(); + if ((power_exponent & mask) != 0) { + MultiplyByUInt32(base); + } + mask >>= 1; + } + + // And finally add the saved shifts. + ShiftLeft(shifts * power_exponent); +} + + +uint16_t Bignum::DivideModuloIntBignum(const Bignum& other) { + ASSERT(IsClamped()); + ASSERT(other.IsClamped()); + ASSERT(other.used_digits_ > 0); + + if (BigitLength() < other.BigitLength()) { + return 0; + } + + Align(other); + + uint16_t result = 0; + + while (BigitLength() > other.BigitLength()) { + ASSERT(other.bigits_[other.used_digits_ - 1] >= ((1 << kBigitSize) / 16)); + ASSERT(bigits_[used_digits_ - 1] < 0x10000); + result += static_cast<uint16_t>(bigits_[used_digits_ - 1]); + SubtractTimes(other, bigits_[used_digits_ - 1]); + } + + ASSERT(BigitLength() == other.BigitLength()); + + Chunk this_bigit = bigits_[used_digits_ - 1]; + Chunk other_bigit = other.bigits_[other.used_digits_ - 1]; + + if (other.used_digits_ == 1) { + int quotient = this_bigit / other_bigit; + bigits_[used_digits_ - 1] = this_bigit - other_bigit * quotient; + ASSERT(quotient < 0x10000); + result += static_cast<uint16_t>(quotient); + Clamp(); + return result; + } + + int division_estimate = this_bigit / (other_bigit + 1); + ASSERT(division_estimate < 0x10000); + result += static_cast<uint16_t>(division_estimate); + SubtractTimes(other, division_estimate); + + if (other_bigit * (division_estimate + 1) > this_bigit) { + return result; + } + + while (LessEqual(other, *this)) { + SubtractBignum(other); + result++; + } + return result; +} + + +template<typename S> +static int SizeInHexChars(S number) { + ASSERT(number > 0); + int result = 0; + while (number != 0) { + number >>= 4; + result++; + } + return result; +} + + +static char HexCharOfValue(int value) { + ASSERT(0 <= value && value <= 16); + if (value < 10) return static_cast<char>(value + '0'); + return static_cast<char>(value - 10 + 'A'); +} + + +bool Bignum::ToHexString(char* buffer, int buffer_size) const { + ASSERT(IsClamped()); + // Each bigit must be printable as separate hex-character. + ASSERT(kBigitSize % 4 == 0); + const int kHexCharsPerBigit = kBigitSize / 4; + + if (used_digits_ == 0) { + if (buffer_size < 2) return false; + buffer[0] = '0'; + buffer[1] = '\0'; + return true; + } + // We add 1 for the terminating '\0' character. + int needed_chars = (BigitLength() - 1) * kHexCharsPerBigit + + SizeInHexChars(bigits_[used_digits_ - 1]) + 1; + if (needed_chars > buffer_size) return false; + int string_index = needed_chars - 1; + buffer[string_index--] = '\0'; + for (int i = 0; i < exponent_; ++i) { + for (int j = 0; j < kHexCharsPerBigit; ++j) { + buffer[string_index--] = '0'; + } + } + for (int i = 0; i < used_digits_ - 1; ++i) { + Chunk current_bigit = bigits_[i]; + for (int j = 0; j < kHexCharsPerBigit; ++j) { + buffer[string_index--] = HexCharOfValue(current_bigit & 0xF); + current_bigit >>= 4; + } + } + // And finally the last bigit. + Chunk most_significant_bigit = bigits_[used_digits_ - 1]; + while (most_significant_bigit != 0) { + buffer[string_index--] = HexCharOfValue(most_significant_bigit & 0xF); + most_significant_bigit >>= 4; + } + return true; +} + + +Bignum::Chunk Bignum::BigitAt(int index) const { + if (index >= BigitLength()) return 0; + if (index < exponent_) return 0; + return bigits_[index - exponent_]; +} + + +int Bignum::Compare(const Bignum& a, const Bignum& b) { + ASSERT(a.IsClamped()); + ASSERT(b.IsClamped()); + int bigit_length_a = a.BigitLength(); + int bigit_length_b = b.BigitLength(); + if (bigit_length_a < bigit_length_b) return -1; + if (bigit_length_a > bigit_length_b) return +1; + for (int i = bigit_length_a - 1; i >= std::min(a.exponent_, b.exponent_); --i) { + Chunk bigit_a = a.BigitAt(i); + Chunk bigit_b = b.BigitAt(i); + if (bigit_a < bigit_b) return -1; + if (bigit_a > bigit_b) return +1; + } + return 0; +} + + +int Bignum::PlusCompare(const Bignum& a, const Bignum& b, const Bignum& c) { + ASSERT(a.IsClamped()); + ASSERT(b.IsClamped()); + ASSERT(c.IsClamped()); + if (a.BigitLength() < b.BigitLength()) { + return PlusCompare(b, a, c); + } + if (a.BigitLength() + 1 < c.BigitLength()) return -1; + if (a.BigitLength() > c.BigitLength()) return +1; + + if (a.exponent_ >= b.BigitLength() && a.BigitLength() < c.BigitLength()) { + return -1; + } + + Chunk borrow = 0; + // Starting at min_exponent all digits are == 0. So no need to compare them. + int min_exponent = std::min(std::min(a.exponent_, b.exponent_), c.exponent_); + for (int i = c.BigitLength() - 1; i >= min_exponent; --i) { + Chunk chunk_a = a.BigitAt(i); + Chunk chunk_b = b.BigitAt(i); + Chunk chunk_c = c.BigitAt(i); + Chunk sum = chunk_a + chunk_b; + if (sum > chunk_c + borrow) { + return +1; + } else { + borrow = chunk_c + borrow - sum; + if (borrow > 1) return -1; + borrow <<= kBigitSize; + } + } + if (borrow == 0) return 0; + return -1; +} + + +void Bignum::Clamp() { + while (used_digits_ > 0 && bigits_[used_digits_ - 1] == 0) { + used_digits_--; + } + if (used_digits_ == 0) { + // Zero. + exponent_ = 0; + } +} + + +bool Bignum::IsClamped() const { + return used_digits_ == 0 || bigits_[used_digits_ - 1] != 0; +} + + +void Bignum::Zero() { + for (int i = 0; i < used_digits_; ++i) { + bigits_[i] = 0; + } + used_digits_ = 0; + exponent_ = 0; +} + + +void Bignum::Align(const Bignum& other) { + if (exponent_ > other.exponent_) { + int zero_digits = exponent_ - other.exponent_; + EnsureCapacity(used_digits_ + zero_digits); + for (int i = used_digits_ - 1; i >= 0; --i) { + bigits_[i + zero_digits] = bigits_[i]; + } + for (int i = 0; i < zero_digits; ++i) { + bigits_[i] = 0; + } + used_digits_ += zero_digits; + exponent_ -= zero_digits; + ASSERT(used_digits_ >= 0); + ASSERT(exponent_ >= 0); + } +} + + +void Bignum::BigitsShiftLeft(int shift_amount) { + ASSERT(shift_amount < kBigitSize); + ASSERT(shift_amount >= 0); + Chunk carry = 0; + for (int i = 0; i < used_digits_; ++i) { + Chunk new_carry = bigits_[i] >> (kBigitSize - shift_amount); + bigits_[i] = ((bigits_[i] << shift_amount) + carry) & kBigitMask; + carry = new_carry; + } + if (carry != 0) { + bigits_[used_digits_] = carry; + used_digits_++; + } +} + + +void Bignum::SubtractTimes(const Bignum& other, int factor) { + ASSERT(exponent_ <= other.exponent_); + if (factor < 3) { + for (int i = 0; i < factor; ++i) { + SubtractBignum(other); + } + return; + } + Chunk borrow = 0; + int exponent_diff = other.exponent_ - exponent_; + for (int i = 0; i < other.used_digits_; ++i) { + DoubleChunk product = static_cast<DoubleChunk>(factor) * other.bigits_[i]; + DoubleChunk remove = borrow + product; + Chunk difference = bigits_[i + exponent_diff] - (remove & kBigitMask); + bigits_[i + exponent_diff] = difference & kBigitMask; + borrow = static_cast<Chunk>((difference >> (kChunkSize - 1)) + + (remove >> kBigitSize)); + } + for (int i = other.used_digits_ + exponent_diff; i < used_digits_; ++i) { + if (borrow == 0) return; + Chunk difference = bigits_[i] - borrow; + bigits_[i] = difference & kBigitMask; + borrow = difference >> (kChunkSize - 1); + } + Clamp(); +} + +class PowersOfTenCache { +public: + static const int kDecimalExponentDistance; + + static const int kMinDecimalExponent; + static const int kMaxDecimalExponent; + + static void GetCachedPowerForBinaryExponentRange(int min_exponent, + int max_exponent, + DiyFp* power, + int* decimal_exponent); + + static void GetCachedPowerForDecimalExponent(int requested_exponent, + DiyFp* power, + int* found_exponent); +}; + +struct CachedPower { + uint64_t significand; + int16_t binary_exponent; + int16_t decimal_exponent; +}; + +static const CachedPower kCachedPowers[] = { + {UINT64_2PART_C(0xfa8fd5a0, 081c0288), -1220, -348}, + {UINT64_2PART_C(0xbaaee17f, a23ebf76), -1193, -340}, + {UINT64_2PART_C(0x8b16fb20, 3055ac76), -1166, -332}, + {UINT64_2PART_C(0xcf42894a, 5dce35ea), -1140, -324}, + {UINT64_2PART_C(0x9a6bb0aa, 55653b2d), -1113, -316}, + {UINT64_2PART_C(0xe61acf03, 3d1a45df), -1087, -308}, + {UINT64_2PART_C(0xab70fe17, c79ac6ca), -1060, -300}, + {UINT64_2PART_C(0xff77b1fc, bebcdc4f), -1034, -292}, + {UINT64_2PART_C(0xbe5691ef, 416bd60c), -1007, -284}, + {UINT64_2PART_C(0x8dd01fad, 907ffc3c), -980, -276}, + {UINT64_2PART_C(0xd3515c28, 31559a83), -954, -268}, + {UINT64_2PART_C(0x9d71ac8f, ada6c9b5), -927, -260}, + {UINT64_2PART_C(0xea9c2277, 23ee8bcb), -901, -252}, + {UINT64_2PART_C(0xaecc4991, 4078536d), -874, -244}, + {UINT64_2PART_C(0x823c1279, 5db6ce57), -847, -236}, + {UINT64_2PART_C(0xc2109436, 4dfb5637), -821, -228}, + {UINT64_2PART_C(0x9096ea6f, 3848984f), -794, -220}, + {UINT64_2PART_C(0xd77485cb, 25823ac7), -768, -212}, + {UINT64_2PART_C(0xa086cfcd, 97bf97f4), -741, -204}, + {UINT64_2PART_C(0xef340a98, 172aace5), -715, -196}, + {UINT64_2PART_C(0xb23867fb, 2a35b28e), -688, -188}, + {UINT64_2PART_C(0x84c8d4df, d2c63f3b), -661, -180}, + {UINT64_2PART_C(0xc5dd4427, 1ad3cdba), -635, -172}, + {UINT64_2PART_C(0x936b9fce, bb25c996), -608, -164}, + {UINT64_2PART_C(0xdbac6c24, 7d62a584), -582, -156}, + {UINT64_2PART_C(0xa3ab6658, 0d5fdaf6), -555, -148}, + {UINT64_2PART_C(0xf3e2f893, dec3f126), -529, -140}, + {UINT64_2PART_C(0xb5b5ada8, aaff80b8), -502, -132}, + {UINT64_2PART_C(0x87625f05, 6c7c4a8b), -475, -124}, + {UINT64_2PART_C(0xc9bcff60, 34c13053), -449, -116}, + {UINT64_2PART_C(0x964e858c, 91ba2655), -422, -108}, + {UINT64_2PART_C(0xdff97724, 70297ebd), -396, -100}, + {UINT64_2PART_C(0xa6dfbd9f, b8e5b88f), -369, -92}, + {UINT64_2PART_C(0xf8a95fcf, 88747d94), -343, -84}, + {UINT64_2PART_C(0xb9447093, 8fa89bcf), -316, -76}, + {UINT64_2PART_C(0x8a08f0f8, bf0f156b), -289, -68}, + {UINT64_2PART_C(0xcdb02555, 653131b6), -263, -60}, + {UINT64_2PART_C(0x993fe2c6, d07b7fac), -236, -52}, + {UINT64_2PART_C(0xe45c10c4, 2a2b3b06), -210, -44}, + {UINT64_2PART_C(0xaa242499, 697392d3), -183, -36}, + {UINT64_2PART_C(0xfd87b5f2, 8300ca0e), -157, -28}, + {UINT64_2PART_C(0xbce50864, 92111aeb), -130, -20}, + {UINT64_2PART_C(0x8cbccc09, 6f5088cc), -103, -12}, + {UINT64_2PART_C(0xd1b71758, e219652c), -77, -4}, + {UINT64_2PART_C(0x9c400000, 00000000), -50, 4}, + {UINT64_2PART_C(0xe8d4a510, 00000000), -24, 12}, + {UINT64_2PART_C(0xad78ebc5, ac620000), 3, 20}, + {UINT64_2PART_C(0x813f3978, f8940984), 30, 28}, + {UINT64_2PART_C(0xc097ce7b, c90715b3), 56, 36}, + {UINT64_2PART_C(0x8f7e32ce, 7bea5c70), 83, 44}, + {UINT64_2PART_C(0xd5d238a4, abe98068), 109, 52}, + {UINT64_2PART_C(0x9f4f2726, 179a2245), 136, 60}, + {UINT64_2PART_C(0xed63a231, d4c4fb27), 162, 68}, + {UINT64_2PART_C(0xb0de6538, 8cc8ada8), 189, 76}, + {UINT64_2PART_C(0x83c7088e, 1aab65db), 216, 84}, + {UINT64_2PART_C(0xc45d1df9, 42711d9a), 242, 92}, + {UINT64_2PART_C(0x924d692c, a61be758), 269, 100}, + {UINT64_2PART_C(0xda01ee64, 1a708dea), 295, 108}, + {UINT64_2PART_C(0xa26da399, 9aef774a), 322, 116}, + {UINT64_2PART_C(0xf209787b, b47d6b85), 348, 124}, + {UINT64_2PART_C(0xb454e4a1, 79dd1877), 375, 132}, + {UINT64_2PART_C(0x865b8692, 5b9bc5c2), 402, 140}, + {UINT64_2PART_C(0xc83553c5, c8965d3d), 428, 148}, + {UINT64_2PART_C(0x952ab45c, fa97a0b3), 455, 156}, + {UINT64_2PART_C(0xde469fbd, 99a05fe3), 481, 164}, + {UINT64_2PART_C(0xa59bc234, db398c25), 508, 172}, + {UINT64_2PART_C(0xf6c69a72, a3989f5c), 534, 180}, + {UINT64_2PART_C(0xb7dcbf53, 54e9bece), 561, 188}, + {UINT64_2PART_C(0x88fcf317, f22241e2), 588, 196}, + {UINT64_2PART_C(0xcc20ce9b, d35c78a5), 614, 204}, + {UINT64_2PART_C(0x98165af3, 7b2153df), 641, 212}, + {UINT64_2PART_C(0xe2a0b5dc, 971f303a), 667, 220}, + {UINT64_2PART_C(0xa8d9d153, 5ce3b396), 694, 228}, + {UINT64_2PART_C(0xfb9b7cd9, a4a7443c), 720, 236}, + {UINT64_2PART_C(0xbb764c4c, a7a44410), 747, 244}, + {UINT64_2PART_C(0x8bab8eef, b6409c1a), 774, 252}, + {UINT64_2PART_C(0xd01fef10, a657842c), 800, 260}, + {UINT64_2PART_C(0x9b10a4e5, e9913129), 827, 268}, + {UINT64_2PART_C(0xe7109bfb, a19c0c9d), 853, 276}, + {UINT64_2PART_C(0xac2820d9, 623bf429), 880, 284}, + {UINT64_2PART_C(0x80444b5e, 7aa7cf85), 907, 292}, + {UINT64_2PART_C(0xbf21e440, 03acdd2d), 933, 300}, + {UINT64_2PART_C(0x8e679c2f, 5e44ff8f), 960, 308}, + {UINT64_2PART_C(0xd433179d, 9c8cb841), 986, 316}, + {UINT64_2PART_C(0x9e19db92, b4e31ba9), 1013, 324}, + {UINT64_2PART_C(0xeb96bf6e, badf77d9), 1039, 332}, + {UINT64_2PART_C(0xaf87023b, 9bf0ee6b), 1066, 340}, +}; + +static const int kCachedPowersLength = ARRAY_SIZE(kCachedPowers); +static const int kCachedPowersOffset = 348; // -1 * the first decimal_exponent. +static const double kD_1_LOG2_10 = 0.30102999566398114; // 1 / lg(10) +// Difference between the decimal exponents in the table above. +const int PowersOfTenCache::kDecimalExponentDistance = 8; +const int PowersOfTenCache::kMinDecimalExponent = -348; +const int PowersOfTenCache::kMaxDecimalExponent = 340; + +void PowersOfTenCache::GetCachedPowerForBinaryExponentRange( + int min_exponent, + int max_exponent, + DiyFp* power, + int* decimal_exponent) { + int kQ = DiyFp::kSignificandSize; + double k = ceil((min_exponent + kQ - 1) * kD_1_LOG2_10); + int foo = kCachedPowersOffset; + int index = + (foo + static_cast<int>(k) - 1) / kDecimalExponentDistance + 1; + ASSERT(0 <= index && index < kCachedPowersLength); + CachedPower cached_power = kCachedPowers[index]; + ASSERT(min_exponent <= cached_power.binary_exponent); + (void) max_exponent; // Mark variable as used. + ASSERT(cached_power.binary_exponent <= max_exponent); + *decimal_exponent = cached_power.decimal_exponent; + *power = DiyFp(cached_power.significand, cached_power.binary_exponent); +} + + +void PowersOfTenCache::GetCachedPowerForDecimalExponent(int requested_exponent, + DiyFp* power, + int* found_exponent) { + ASSERT(kMinDecimalExponent <= requested_exponent); + ASSERT(requested_exponent < kMaxDecimalExponent + kDecimalExponentDistance); + int index = + (requested_exponent + kCachedPowersOffset) / kDecimalExponentDistance; + CachedPower cached_power = kCachedPowers[index]; + *power = DiyFp(cached_power.significand, cached_power.binary_exponent); + *found_exponent = cached_power.decimal_exponent; + ASSERT(*found_exponent <= requested_exponent); + ASSERT(requested_exponent < *found_exponent + kDecimalExponentDistance); +} + +enum BignumDtoaMode { + BIGNUM_DTOA_SHORTEST, + BIGNUM_DTOA_FIXED, + BIGNUM_DTOA_PRECISION +}; + +static int NormalizedExponent(uint64_t significand, int exponent) { + ASSERT(significand != 0); + while ((significand & Double::kHiddenBit) == 0) { + significand = significand << 1; + exponent = exponent - 1; + } + return exponent; +} + +static int EstimatePower(int exponent); + +static void InitialScaledStartValues(uint64_t significand, + int exponent, + bool lower_boundary_is_closer, + int estimated_power, + bool need_boundary_deltas, + Bignum* numerator, + Bignum* denominator, + Bignum* delta_minus, + Bignum* delta_plus); + +static void FixupMultiply10(int estimated_power, bool is_even, + int* decimal_point, + Bignum* numerator, Bignum* denominator, + Bignum* delta_minus, Bignum* delta_plus); + +static void GenerateShortestDigits(Bignum* numerator, Bignum* denominator, + Bignum* delta_minus, Bignum* delta_plus, + bool is_even, + Vector<char> buffer, int* length); + +static void BignumToFixed(int requested_digits, int* decimal_point, + Bignum* numerator, Bignum* denominator, + Vector<char>(buffer), int* length); + +static void GenerateCountedDigits(int count, int* decimal_point, + Bignum* numerator, Bignum* denominator, + Vector<char>(buffer), int* length); + + +void BignumDtoa(double v, BignumDtoaMode mode, int requested_digits, + Vector<char> buffer, int* length, int* decimal_point) { + ASSERT(v > 0); + ASSERT(!Double(v).IsSpecial()); + uint64_t significand; + int exponent; + bool lower_boundary_is_closer; + + significand = Double(v).Significand(); + exponent = Double(v).Exponent(); + lower_boundary_is_closer = Double(v).LowerBoundaryIsCloser(); + + bool need_boundary_deltas = + (mode == BIGNUM_DTOA_SHORTEST); + + bool is_even = (significand & 1) == 0; + int normalized_exponent = NormalizedExponent(significand, exponent); + // estimated_power might be too low by 1. + int estimated_power = EstimatePower(normalized_exponent); + + if (mode == BIGNUM_DTOA_FIXED && -estimated_power - 1 > requested_digits) { + buffer[0] = '\0'; + *length = 0; + *decimal_point = -requested_digits; + return; + } + + Bignum numerator; + Bignum denominator; + Bignum delta_minus; + Bignum delta_plus; + + ASSERT(Bignum::kMaxSignificantBits >= 324*4); + InitialScaledStartValues(significand, exponent, lower_boundary_is_closer, + estimated_power, need_boundary_deltas, + &numerator, &denominator, + &delta_minus, &delta_plus); + + FixupMultiply10(estimated_power, is_even, decimal_point, + &numerator, &denominator, + &delta_minus, &delta_plus); + + switch (mode) { + case BIGNUM_DTOA_SHORTEST: + GenerateShortestDigits(&numerator, &denominator, + &delta_minus, &delta_plus, + is_even, buffer, length); + break; + case BIGNUM_DTOA_FIXED: + BignumToFixed(requested_digits, decimal_point, + &numerator, &denominator, + buffer, length); + break; + case BIGNUM_DTOA_PRECISION: + GenerateCountedDigits(requested_digits, decimal_point, + &numerator, &denominator, + buffer, length); + break; + default: + UNREACHABLE(); + } + buffer[*length] = '\0'; +} + +static void GenerateShortestDigits(Bignum* numerator, Bignum* denominator, + Bignum* delta_minus, Bignum* delta_plus, + bool is_even, + Vector<char> buffer, int* length) { + if (Bignum::Equal(*delta_minus, *delta_plus)) { + delta_plus = delta_minus; + } + *length = 0; + for (;;) { + uint16_t digit; + digit = numerator->DivideModuloIntBignum(*denominator); + ASSERT(digit <= 9); + + buffer[(*length)++] = static_cast<char>(digit + '0'); + + bool in_delta_room_minus; + bool in_delta_room_plus; + if (is_even) { + in_delta_room_minus = Bignum::LessEqual(*numerator, *delta_minus); + } else { + in_delta_room_minus = Bignum::Less(*numerator, *delta_minus); + } + if (is_even) { + in_delta_room_plus = + Bignum::PlusCompare(*numerator, *delta_plus, *denominator) >= 0; + } else { + in_delta_room_plus = + Bignum::PlusCompare(*numerator, *delta_plus, *denominator) > 0; + } + if (!in_delta_room_minus && !in_delta_room_plus) { + numerator->Times10(); + delta_minus->Times10(); + + if (delta_minus != delta_plus) { + delta_plus->Times10(); + } + } else if (in_delta_room_minus && in_delta_room_plus) { + + int compare = Bignum::PlusCompare(*numerator, *numerator, *denominator); + if (compare < 0) { + // Remaining digits are less than .5. -> Round down (== do nothing). + } else if (compare > 0) { + // Remaining digits are more than .5 of denominator. -> Round up. + ASSERT(buffer[(*length) - 1] != '9'); + buffer[(*length) - 1]++; + } else { + if ((buffer[(*length) - 1] - '0') % 2 == 0) { + // Round down => Do nothing. + } else { + ASSERT(buffer[(*length) - 1] != '9'); + buffer[(*length) - 1]++; + } + } + return; + } else if (in_delta_room_minus) { + return; + } else { // in_delta_room_plus + // Round up + ASSERT(buffer[(*length) -1] != '9'); + buffer[(*length) - 1]++; + return; + } + } +} + +static void GenerateCountedDigits(int count, int* decimal_point, + Bignum* numerator, Bignum* denominator, + Vector<char> buffer, int* length) { + ASSERT(count >= 0); + for (int i = 0; i < count - 1; ++i) { + uint16_t digit; + digit = numerator->DivideModuloIntBignum(*denominator); + ASSERT(digit <= 9); + + buffer[i] = static_cast<char>(digit + '0'); + numerator->Times10(); + } + + uint16_t digit; + digit = numerator->DivideModuloIntBignum(*denominator); + if (Bignum::PlusCompare(*numerator, *numerator, *denominator) >= 0) { + digit++; + } + ASSERT(digit <= 10); + buffer[count - 1] = static_cast<char>(digit + '0'); + + for (int i = count - 1; i > 0; --i) { + if (buffer[i] != '0' + 10) break; + buffer[i] = '0'; + buffer[i - 1]++; + } + if (buffer[0] == '0' + 10) { + buffer[0] = '1'; + (*decimal_point)++; + } + *length = count; +} + +static void BignumToFixed(int requested_digits, int* decimal_point, + Bignum* numerator, Bignum* denominator, + Vector<char>(buffer), int* length) +{ + if (-(*decimal_point) > requested_digits) { + *decimal_point = -requested_digits; + *length = 0; + return; + } else if (-(*decimal_point) == requested_digits) { + ASSERT(*decimal_point == -requested_digits); + + denominator->Times10(); + if (Bignum::PlusCompare(*numerator, *numerator, *denominator) >= 0) { + buffer[0] = '1'; + *length = 1; + (*decimal_point)++; + } else { + *length = 0; + } + return; + } else { + int needed_digits = (*decimal_point) + requested_digits; + GenerateCountedDigits(needed_digits, decimal_point, + numerator, denominator, + buffer, length); + } +} + +static int EstimatePower(int exponent) { + const double k1Log10 = 0.30102999566398114; // 1/lg(10) + + // For doubles len(f) == 53 (don't forget the hidden bit). + const int kSignificandSize = Double::kSignificandSize; + double estimate = ceil((exponent + kSignificandSize - 1) * k1Log10 - 1e-10); + return static_cast<int>(estimate); +} + +static void InitialScaledStartValuesPositiveExponent( + uint64_t significand, int exponent, + int estimated_power, bool need_boundary_deltas, + Bignum* numerator, Bignum* denominator, + Bignum* delta_minus, Bignum* delta_plus) +{ + ASSERT(estimated_power >= 0); + + numerator->AssignUInt64(significand); + numerator->ShiftLeft(exponent); + denominator->AssignPowerUInt16(10, estimated_power); + + if (need_boundary_deltas) { + denominator->ShiftLeft(1); + numerator->ShiftLeft(1); + delta_plus->AssignUInt16(1); + delta_plus->ShiftLeft(exponent); + delta_minus->AssignUInt16(1); + delta_minus->ShiftLeft(exponent); + } +} + +static void InitialScaledStartValuesNegativeExponentPositivePower( + uint64_t significand, int exponent, + int estimated_power, bool need_boundary_deltas, + Bignum* numerator, Bignum* denominator, + Bignum* delta_minus, Bignum* delta_plus) +{ + numerator->AssignUInt64(significand); + denominator->AssignPowerUInt16(10, estimated_power); + denominator->ShiftLeft(-exponent); + + if (need_boundary_deltas) { + denominator->ShiftLeft(1); + numerator->ShiftLeft(1); + delta_plus->AssignUInt16(1); + delta_minus->AssignUInt16(1); + } +} + +static void InitialScaledStartValuesNegativeExponentNegativePower( + uint64_t significand, int exponent, + int estimated_power, bool need_boundary_deltas, + Bignum* numerator, Bignum* denominator, + Bignum* delta_minus, Bignum* delta_plus) +{ + Bignum* power_ten = numerator; + power_ten->AssignPowerUInt16(10, -estimated_power); + + if (need_boundary_deltas) { + delta_plus->AssignBignum(*power_ten); + delta_minus->AssignBignum(*power_ten); + } + + ASSERT(numerator == power_ten); + numerator->MultiplyByUInt64(significand); + + denominator->AssignUInt16(1); + denominator->ShiftLeft(-exponent); + + if (need_boundary_deltas) { + numerator->ShiftLeft(1); + denominator->ShiftLeft(1); + } +} + +static void InitialScaledStartValues(uint64_t significand, + int exponent, + bool lower_boundary_is_closer, + int estimated_power, + bool need_boundary_deltas, + Bignum* numerator, + Bignum* denominator, + Bignum* delta_minus, + Bignum* delta_plus) +{ + if (exponent >= 0) { + InitialScaledStartValuesPositiveExponent( + significand, exponent, estimated_power, need_boundary_deltas, + numerator, denominator, delta_minus, delta_plus); + } else if (estimated_power >= 0) { + InitialScaledStartValuesNegativeExponentPositivePower( + significand, exponent, estimated_power, need_boundary_deltas, + numerator, denominator, delta_minus, delta_plus); + } else { + InitialScaledStartValuesNegativeExponentNegativePower( + significand, exponent, estimated_power, need_boundary_deltas, + numerator, denominator, delta_minus, delta_plus); + } + + if (need_boundary_deltas && lower_boundary_is_closer) { + denominator->ShiftLeft(1); // *2 + numerator->ShiftLeft(1); // *2 + delta_plus->ShiftLeft(1); // *2 + } +} + +static void FixupMultiply10(int estimated_power, bool is_even, + int* decimal_point, + Bignum* numerator, Bignum* denominator, + Bignum* delta_minus, Bignum* delta_plus) { + bool in_range; + if (is_even) { + in_range = Bignum::PlusCompare(*numerator, *delta_plus, *denominator) >= 0; + } else { + in_range = Bignum::PlusCompare(*numerator, *delta_plus, *denominator) > 0; + } + if (in_range) { + *decimal_point = estimated_power + 1; + } else { + *decimal_point = estimated_power; + numerator->Times10(); + if (Bignum::Equal(*delta_minus, *delta_plus)) { + delta_minus->Times10(); + delta_plus->AssignBignum(*delta_minus); + } else { + delta_minus->Times10(); + delta_plus->Times10(); + } + } +} + +enum FastDtoaMode { + FAST_DTOA_SHORTEST, + FAST_DTOA_PRECISION +}; + +static const int kFastDtoaMaximalLength = 17; + +bool FastDtoa(double d, + FastDtoaMode mode, + int requested_digits, + Vector<char> buffer, + int* length, + int* decimal_point); + +static const int kMinimalTargetExponent = -60; +static const int kMaximalTargetExponent = -32; + +static bool RoundWeed(Vector<char> buffer, int length, + uint64_t distance_too_high_w, uint64_t unsafe_interval, + uint64_t rest, uint64_t ten_kappa, uint64_t unit) +{ + uint64_t small_distance = distance_too_high_w - unit; + uint64_t big_distance = distance_too_high_w + unit; + + ASSERT(rest <= unsafe_interval); + while (rest < small_distance && // Negated condition 1 + unsafe_interval - rest >= ten_kappa && // Negated condition 2 + (rest + ten_kappa < small_distance || // buffer{-1} > w_high + small_distance - rest >= rest + ten_kappa - small_distance)) { + buffer[length - 1]--; + rest += ten_kappa; + } + + if (rest < big_distance && + unsafe_interval - rest >= ten_kappa && + (rest + ten_kappa < big_distance || + big_distance - rest > rest + ten_kappa - big_distance)) { + return false; + } + + return (2 * unit <= rest) && (rest <= unsafe_interval - 4 * unit); +} + +static bool RoundWeedCounted(Vector<char> buffer, int length, + uint64_t rest, uint64_t ten_kappa, uint64_t unit, + int* kappa) +{ + ASSERT(rest < ten_kappa); + + if (unit >= ten_kappa) return false; + if (ten_kappa - unit <= unit) return false; + if ((ten_kappa - rest > rest) && (ten_kappa - 2 * rest >= 2 * unit)) { + return true; + } + + if ((rest > unit) && (ten_kappa - (rest - unit) <= (rest - unit))) { + buffer[length - 1]++; + for (int i = length - 1; i > 0; --i) { + if (buffer[i] != '0' + 10) break; + buffer[i] = '0'; + buffer[i - 1]++; + } + if (buffer[0] == '0' + 10) { + buffer[0] = '1'; + (*kappa) += 1; + } + return true; + } + return false; +} + +static unsigned int const kSmallPowersOfTen[] = + {0, 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, + 1000000000}; + +static void BiggestPowerTen(uint32_t number, + int number_bits, + uint32_t* power, + int* exponent_plus_one) { + ASSERT(number < (1u << (number_bits + 1))); + + int exponent_plus_one_guess = ((number_bits + 1) * 1233 >> 12); + exponent_plus_one_guess++; + + if (number < kSmallPowersOfTen[exponent_plus_one_guess]) { + exponent_plus_one_guess--; + } + *power = kSmallPowersOfTen[exponent_plus_one_guess]; + *exponent_plus_one = exponent_plus_one_guess; +} + +static bool DigitGen(DiyFp low, DiyFp w, DiyFp high, Vector<char> buffer, + int* length, int* kappa) +{ + ASSERT(low.e() == w.e() && w.e() == high.e()); + ASSERT(low.f() + 1 <= high.f() - 1); + ASSERT(kMinimalTargetExponent <= w.e() && w.e() <= kMaximalTargetExponent); + + uint64_t unit = 1; + DiyFp too_low = DiyFp(low.f() - unit, low.e()); + DiyFp too_high = DiyFp(high.f() + unit, high.e()); + DiyFp unsafe_interval = DiyFp::Minus(too_high, too_low); + DiyFp one = DiyFp(static_cast<uint64_t>(1) << -w.e(), w.e()); + + uint32_t integrals = static_cast<uint32_t>(too_high.f() >> -one.e()); + uint64_t fractionals = too_high.f() & (one.f() - 1); + uint32_t divisor; + int divisor_exponent_plus_one; + BiggestPowerTen(integrals, DiyFp::kSignificandSize - (-one.e()), + &divisor, &divisor_exponent_plus_one); + *kappa = divisor_exponent_plus_one; + *length = 0; + + while (*kappa > 0) { + int digit = integrals / divisor; + ASSERT(digit <= 9); + buffer[*length] = static_cast<char>('0' + digit); + (*length)++; + integrals %= divisor; + (*kappa)--; + + uint64_t rest = + (static_cast<uint64_t>(integrals) << -one.e()) + fractionals; + + if (rest < unsafe_interval.f()) { + return RoundWeed(buffer, *length, DiyFp::Minus(too_high, w).f(), + unsafe_interval.f(), rest, + static_cast<uint64_t>(divisor) << -one.e(), unit); + } + divisor /= 10; + } + + ASSERT(one.e() >= -60); + ASSERT(fractionals < one.f()); + ASSERT(UINT64_2PART_C(0xFFFFFFFF, FFFFFFFF) / 10 >= one.f()); + + for (;;) { + fractionals *= 10; + unit *= 10; + unsafe_interval.set_f(unsafe_interval.f() * 10); + // Integer division by one. + int digit = static_cast<int>(fractionals >> -one.e()); + ASSERT(digit <= 9); + buffer[*length] = static_cast<char>('0' + digit); + (*length)++; + fractionals &= one.f() - 1; // Modulo by one. + (*kappa)--; + if (fractionals < unsafe_interval.f()) { + return RoundWeed(buffer, *length, DiyFp::Minus(too_high, w).f() * unit, + unsafe_interval.f(), fractionals, one.f(), unit); + } + } +} + +static bool DigitGenCounted(DiyFp w, int requested_digits, Vector<char> buffer, + int* length, int* kappa) +{ + ASSERT(kMinimalTargetExponent <= w.e() && w.e() <= kMaximalTargetExponent); + ASSERT(kMinimalTargetExponent >= -60); + ASSERT(kMaximalTargetExponent <= -32); + + uint64_t w_error = 1; + DiyFp one = DiyFp(static_cast<uint64_t>(1) << -w.e(), w.e()); + uint32_t integrals = static_cast<uint32_t>(w.f() >> -one.e()); + uint64_t fractionals = w.f() & (one.f() - 1); + uint32_t divisor; + int divisor_exponent_plus_one; + BiggestPowerTen(integrals, DiyFp::kSignificandSize - (-one.e()), + &divisor, &divisor_exponent_plus_one); + *kappa = divisor_exponent_plus_one; + *length = 0; + + while (*kappa > 0) { + int digit = integrals / divisor; + ASSERT(digit <= 9); + buffer[*length] = static_cast<char>('0' + digit); + (*length)++; + requested_digits--; + integrals %= divisor; + (*kappa)--; + if (requested_digits == 0) break; + divisor /= 10; + } + + if (requested_digits == 0) { + uint64_t rest = + (static_cast<uint64_t>(integrals) << -one.e()) + fractionals; + return RoundWeedCounted(buffer, *length, rest, + static_cast<uint64_t>(divisor) << -one.e(), w_error, + kappa); + } + + ASSERT(one.e() >= -60); + ASSERT(fractionals < one.f()); + ASSERT(UINT64_2PART_C(0xFFFFFFFF, FFFFFFFF) / 10 >= one.f()); + + while (requested_digits > 0 && fractionals > w_error) { + fractionals *= 10; + w_error *= 10; + // Integer division by one. + int digit = static_cast<int>(fractionals >> -one.e()); + ASSERT(digit <= 9); + buffer[*length] = static_cast<char>('0' + digit); + (*length)++; + requested_digits--; + fractionals &= one.f() - 1; // Modulo by one. + (*kappa)--; + } + if (requested_digits != 0) return false; + return RoundWeedCounted(buffer, *length, fractionals, one.f(), w_error, + kappa); +} + +static bool Grisu3(double v, FastDtoaMode mode, Vector<char> buffer, + int* length, int* decimal_exponent) +{ + DiyFp w = Double(v).AsNormalizedDiyFp(); + DiyFp boundary_minus, boundary_plus; + + ASSERT(mode == FAST_DTOA_SHORTEST); + Double(v).NormalizedBoundaries(&boundary_minus, &boundary_plus); + + ASSERT(boundary_plus.e() == w.e()); + DiyFp ten_mk; // Cached power of ten: 10^-k + int mk; // -k + int ten_mk_minimal_binary_exponent = + kMinimalTargetExponent - (w.e() + DiyFp::kSignificandSize); + int ten_mk_maximal_binary_exponent = + kMaximalTargetExponent - (w.e() + DiyFp::kSignificandSize); + PowersOfTenCache::GetCachedPowerForBinaryExponentRange( + ten_mk_minimal_binary_exponent, + ten_mk_maximal_binary_exponent, + &ten_mk, &mk); + + ASSERT((kMinimalTargetExponent <= w.e() + ten_mk.e() + + DiyFp::kSignificandSize) && + (kMaximalTargetExponent >= w.e() + ten_mk.e() + + DiyFp::kSignificandSize)); + + DiyFp scaled_w = DiyFp::Times(w, ten_mk); + ASSERT(scaled_w.e() == + boundary_plus.e() + ten_mk.e() + DiyFp::kSignificandSize); + + DiyFp scaled_boundary_minus = DiyFp::Times(boundary_minus, ten_mk); + DiyFp scaled_boundary_plus = DiyFp::Times(boundary_plus, ten_mk); + + int kappa; + bool result = DigitGen(scaled_boundary_minus, scaled_w, scaled_boundary_plus, + buffer, length, &kappa); + *decimal_exponent = -mk + kappa; + return result; +} + +static bool Grisu3Counted(double v, int requested_digits, Vector<char> buffer, + int* length, int* decimal_exponent) +{ + DiyFp w = Double(v).AsNormalizedDiyFp(); + DiyFp ten_mk; // Cached power of ten: 10^-k + int mk; // -k + int ten_mk_minimal_binary_exponent = + kMinimalTargetExponent - (w.e() + DiyFp::kSignificandSize); + int ten_mk_maximal_binary_exponent = + kMaximalTargetExponent - (w.e() + DiyFp::kSignificandSize); + PowersOfTenCache::GetCachedPowerForBinaryExponentRange( + ten_mk_minimal_binary_exponent, + ten_mk_maximal_binary_exponent, + &ten_mk, &mk); + ASSERT((kMinimalTargetExponent <= w.e() + ten_mk.e() + + DiyFp::kSignificandSize) && + (kMaximalTargetExponent >= w.e() + ten_mk.e() + + DiyFp::kSignificandSize)); + + DiyFp scaled_w = DiyFp::Times(w, ten_mk); + + int kappa; + bool result = DigitGenCounted(scaled_w, requested_digits, + buffer, length, &kappa); + *decimal_exponent = -mk + kappa; + return result; +} + + +bool FastDtoa(double v, + FastDtoaMode mode, + int requested_digits, + Vector<char> buffer, + int* length, + int* decimal_point) { + ASSERT(v > 0); + ASSERT(!Double(v).IsSpecial()); + + bool result = false; + int decimal_exponent = 0; + switch (mode) { + case FAST_DTOA_SHORTEST: + result = Grisu3(v, mode, buffer, length, &decimal_exponent); + break; + case FAST_DTOA_PRECISION: + result = Grisu3Counted(v, requested_digits, + buffer, length, &decimal_exponent); + break; + default: + UNREACHABLE(); + } + if (result) { + *decimal_point = *length + decimal_exponent; + buffer[*length] = '\0'; + } + return result; +} + +// Represents a 128bit type. This class should be replaced by a native type on +// platforms that support 128bit integers. +class UInt128 { + public: + UInt128() : high_bits_(0), low_bits_(0) { } + UInt128(uint64_t high, uint64_t low) : high_bits_(high), low_bits_(low) { } + + void Multiply(uint32_t multiplicand) { + uint64_t accumulator; + + accumulator = (low_bits_ & kMask32) * multiplicand; + uint32_t part = static_cast<uint32_t>(accumulator & kMask32); + accumulator >>= 32; + accumulator = accumulator + (low_bits_ >> 32) * multiplicand; + low_bits_ = (accumulator << 32) + part; + accumulator >>= 32; + accumulator = accumulator + (high_bits_ & kMask32) * multiplicand; + part = static_cast<uint32_t>(accumulator & kMask32); + accumulator >>= 32; + accumulator = accumulator + (high_bits_ >> 32) * multiplicand; + high_bits_ = (accumulator << 32) + part; + ASSERT((accumulator >> 32) == 0); + } + + void Shift(int shift_amount) { + ASSERT(-64 <= shift_amount && shift_amount <= 64); + if (shift_amount == 0) { + return; + } else if (shift_amount == -64) { + high_bits_ = low_bits_; + low_bits_ = 0; + } else if (shift_amount == 64) { + low_bits_ = high_bits_; + high_bits_ = 0; + } else if (shift_amount <= 0) { + high_bits_ <<= -shift_amount; + high_bits_ += low_bits_ >> (64 + shift_amount); + low_bits_ <<= -shift_amount; + } else { + low_bits_ >>= shift_amount; + low_bits_ += high_bits_ << (64 - shift_amount); + high_bits_ >>= shift_amount; + } + } + + // Modifies *this to *this MOD (2^power). + // Returns *this DIV (2^power). + int DivModPowerOf2(int power) { + if (power >= 64) { + int result = static_cast<int>(high_bits_ >> (power - 64)); + high_bits_ -= static_cast<uint64_t>(result) << (power - 64); + return result; + } else { + uint64_t part_low = low_bits_ >> power; + uint64_t part_high = high_bits_ << (64 - power); + int result = static_cast<int>(part_low + part_high); + high_bits_ = 0; + low_bits_ -= part_low << power; + return result; + } + } + + bool IsZero() const { + return high_bits_ == 0 && low_bits_ == 0; + } + + int BitAt(int position) { + if (position >= 64) { + return static_cast<int>(high_bits_ >> (position - 64)) & 1; + } else { + return static_cast<int>(low_bits_ >> position) & 1; + } + } + + private: + static const uint64_t kMask32 = 0xFFFFFFFF; + // Value == (high_bits_ << 64) + low_bits_ + uint64_t high_bits_; + uint64_t low_bits_; +}; + + +static const int kDoubleSignificandSize = 53; // Includes the hidden bit. + + +static void FillDigits32FixedLength(uint32_t number, int requested_length, + Vector<char> buffer, int* length) { + for (int i = requested_length - 1; i >= 0; --i) { + buffer[(*length) + i] = '0' + number % 10; + number /= 10; + } + *length += requested_length; +} + + +static void FillDigits32(uint32_t number, Vector<char> buffer, int* length) { + int number_length = 0; + // We fill the digits in reverse order and exchange them afterwards. + while (number != 0) { + int digit = number % 10; + number /= 10; + buffer[(*length) + number_length] = static_cast<char>('0' + digit); + number_length++; + } + // Exchange the digits. + int i = *length; + int j = *length + number_length - 1; + while (i < j) { + char tmp = buffer[i]; + buffer[i] = buffer[j]; + buffer[j] = tmp; + i++; + j--; + } + *length += number_length; +} + + +static void FillDigits64FixedLength(uint64_t number, + Vector<char> buffer, int* length) { + const uint32_t kTen7 = 10000000; + // For efficiency cut the number into 3 uint32_t parts, and print those. + uint32_t part2 = static_cast<uint32_t>(number % kTen7); + number /= kTen7; + uint32_t part1 = static_cast<uint32_t>(number % kTen7); + uint32_t part0 = static_cast<uint32_t>(number / kTen7); + + FillDigits32FixedLength(part0, 3, buffer, length); + FillDigits32FixedLength(part1, 7, buffer, length); + FillDigits32FixedLength(part2, 7, buffer, length); +} + + +static void FillDigits64(uint64_t number, Vector<char> buffer, int* length) { + const uint32_t kTen7 = 10000000; + // For efficiency cut the number into 3 uint32_t parts, and print those. + uint32_t part2 = static_cast<uint32_t>(number % kTen7); + number /= kTen7; + uint32_t part1 = static_cast<uint32_t>(number % kTen7); + uint32_t part0 = static_cast<uint32_t>(number / kTen7); + + if (part0 != 0) { + FillDigits32(part0, buffer, length); + FillDigits32FixedLength(part1, 7, buffer, length); + FillDigits32FixedLength(part2, 7, buffer, length); + } else if (part1 != 0) { + FillDigits32(part1, buffer, length); + FillDigits32FixedLength(part2, 7, buffer, length); + } else { + FillDigits32(part2, buffer, length); + } +} + + +static void RoundUp(Vector<char> buffer, int* length, int* decimal_point) { + // An empty buffer represents 0. + if (*length == 0) { + buffer[0] = '1'; + *decimal_point = 1; + *length = 1; + return; + } + + buffer[(*length) - 1]++; + for (int i = (*length) - 1; i > 0; --i) { + if (buffer[i] != '0' + 10) { + return; + } + buffer[i] = '0'; + buffer[i - 1]++; + } + + if (buffer[0] == '0' + 10) { + buffer[0] = '1'; + (*decimal_point)++; + } +} + +static void FillFractionals(uint64_t fractionals, int exponent, + int fractional_count, Vector<char> buffer, + int* length, int* decimal_point) +{ + ASSERT(-128 <= exponent && exponent <= 0); + + if (-exponent <= 64) { + ASSERT(fractionals >> 56 == 0); + int point = -exponent; + for (int i = 0; i < fractional_count; ++i) { + if (fractionals == 0) break; + fractionals *= 5; + point--; + int digit = static_cast<int>(fractionals >> point); + ASSERT(digit <= 9); + buffer[*length] = static_cast<char>('0' + digit); + (*length)++; + fractionals -= static_cast<uint64_t>(digit) << point; + } + + if (((fractionals >> (point - 1)) & 1) == 1) { + RoundUp(buffer, length, decimal_point); + } + } else { // We need 128 bits. + ASSERT(64 < -exponent && -exponent <= 128); + UInt128 fractionals128 = UInt128(fractionals, 0); + fractionals128.Shift(-exponent - 64); + int point = 128; + for (int i = 0; i < fractional_count; ++i) { + if (fractionals128.IsZero()) break; + fractionals128.Multiply(5); + point--; + int digit = fractionals128.DivModPowerOf2(point); + ASSERT(digit <= 9); + buffer[*length] = static_cast<char>('0' + digit); + (*length)++; + } + if (fractionals128.BitAt(point - 1) == 1) { + RoundUp(buffer, length, decimal_point); + } + } +} + + +// Removes leading and trailing zeros. +// If leading zeros are removed then the decimal point position is adjusted. +static void TrimZeros(Vector<char> buffer, int* length, int* decimal_point) { + while (*length > 0 && buffer[(*length) - 1] == '0') { + (*length)--; + } + int first_non_zero = 0; + while (first_non_zero < *length && buffer[first_non_zero] == '0') { + first_non_zero++; + } + if (first_non_zero != 0) { + for (int i = first_non_zero; i < *length; ++i) { + buffer[i - first_non_zero] = buffer[i]; + } + *length -= first_non_zero; + *decimal_point -= first_non_zero; + } +} + + +bool FastFixedDtoa(double v, + int fractional_count, + Vector<char> buffer, + int* length, + int* decimal_point) { + const uint32_t kMaxUInt32 = 0xFFFFFFFF; + uint64_t significand = Double(v).Significand(); + int exponent = Double(v).Exponent(); + + if (exponent > 20) return false; + if (fractional_count > 20) return false; + *length = 0; + + if (exponent + kDoubleSignificandSize > 64) { + const uint64_t kFive17 = UINT64_2PART_C(0xB1, A2BC2EC5); // 5^17 + uint64_t divisor = kFive17; + int divisor_power = 17; + uint64_t dividend = significand; + uint32_t quotient; + uint64_t remainder; + + if (exponent > divisor_power) { + dividend <<= exponent - divisor_power; + quotient = static_cast<uint32_t>(dividend / divisor); + remainder = (dividend % divisor) << divisor_power; + } else { + divisor <<= divisor_power - exponent; + quotient = static_cast<uint32_t>(dividend / divisor); + remainder = (dividend % divisor) << exponent; + } + FillDigits32(quotient, buffer, length); + FillDigits64FixedLength(remainder, buffer, length); + *decimal_point = *length; + } else if (exponent >= 0) { + // 0 <= exponent <= 11 + significand <<= exponent; + FillDigits64(significand, buffer, length); + *decimal_point = *length; + } else if (exponent > -kDoubleSignificandSize) { + uint64_t integrals = significand >> -exponent; + uint64_t fractionals = significand - (integrals << -exponent); + if (integrals > kMaxUInt32) { + FillDigits64(integrals, buffer, length); + } else { + FillDigits32(static_cast<uint32_t>(integrals), buffer, length); + } + *decimal_point = *length; + FillFractionals(fractionals, exponent, fractional_count, + buffer, length, decimal_point); + } else if (exponent < -128) { + // This configuration (with at most 20 digits) means that all digits must be + // 0. + ASSERT(fractional_count <= 20); + buffer[0] = '\0'; + *length = 0; + *decimal_point = -fractional_count; + } else { + *decimal_point = 0; + FillFractionals(significand, exponent, fractional_count, + buffer, length, decimal_point); + } + TrimZeros(buffer, length, decimal_point); + buffer[*length] = '\0'; + if ((*length) == 0) { + *decimal_point = -fractional_count; + } + return true; +} + +static const int kMaxExactDoubleIntegerDecimalDigits = 15; +static const int kMaxUint64DecimalDigits = 19; + +static const int kMaxDecimalPower = 309; +static const int kMinDecimalPower = -324; + +// 2^64 = 18446744073709551616 +static const uint64_t kMaxUint64 = UINT64_2PART_C(0xFFFFFFFF, FFFFFFFF); +static const int kMaxSignificantDecimalDigits = 780; + +static Vector<const char> TrimLeadingZeros(Vector<const char> buffer) { + for (int i = 0; i < buffer.length(); i++) { + if (buffer[i] != '0') { + return buffer.SubVector(i, buffer.length()); + } + } + return Vector<const char>(buffer.start(), 0); +} + + +static Vector<const char> TrimTrailingZeros(Vector<const char> buffer) { + for (int i = buffer.length() - 1; i >= 0; --i) { + if (buffer[i] != '0') { + return buffer.SubVector(0, i + 1); + } + } + return Vector<const char>(buffer.start(), 0); +} + + +static void CutToMaxSignificantDigits(Vector<const char> buffer, + int exponent, + char* significant_buffer, + int* significant_exponent) { + for (int i = 0; i < kMaxSignificantDecimalDigits - 1; ++i) { + significant_buffer[i] = buffer[i]; + } + + ASSERT(buffer[buffer.length() - 1] != '0'); + + significant_buffer[kMaxSignificantDecimalDigits - 1] = '1'; + *significant_exponent = + exponent + (buffer.length() - kMaxSignificantDecimalDigits); +} + +static void TrimAndCut(Vector<const char> buffer, int exponent, + char* buffer_copy_space, int space_size, + Vector<const char>* trimmed, int* updated_exponent) { + Vector<const char> left_trimmed = TrimLeadingZeros(buffer); + Vector<const char> right_trimmed = TrimTrailingZeros(left_trimmed); + exponent += left_trimmed.length() - right_trimmed.length(); + if (right_trimmed.length() > kMaxSignificantDecimalDigits) { + (void) space_size; // Mark variable as used. + ASSERT(space_size >= kMaxSignificantDecimalDigits); + CutToMaxSignificantDigits(right_trimmed, exponent, + buffer_copy_space, updated_exponent); + *trimmed = Vector<const char>(buffer_copy_space, + kMaxSignificantDecimalDigits); + } else { + *trimmed = right_trimmed; + *updated_exponent = exponent; + } +} + +static uint64_t ReadUint64(Vector<const char> buffer, + int* number_of_read_digits) { + uint64_t result = 0; + int i = 0; + while (i < buffer.length() && result <= (kMaxUint64 / 10 - 1)) { + int digit = buffer[i++] - '0'; + ASSERT(0 <= digit && digit <= 9); + result = 10 * result + digit; + } + *number_of_read_digits = i; + return result; +} + +static void ReadDiyFp(Vector<const char> buffer, + DiyFp* result, + int* remaining_decimals) { + int read_digits; + uint64_t significand = ReadUint64(buffer, &read_digits); + if (buffer.length() == read_digits) { + *result = DiyFp(significand, 0); + *remaining_decimals = 0; + } else { + // Round the significand. + if (buffer[read_digits] >= '5') { + significand++; + } + // Compute the binary exponent. + int exponent = 0; + *result = DiyFp(significand, exponent); + *remaining_decimals = buffer.length() - read_digits; + } +} + +static DiyFp AdjustmentPowerOfTen(int exponent) { + ASSERT(0 < exponent); + ASSERT(exponent < PowersOfTenCache::kDecimalExponentDistance); + // Simply hardcode the remaining powers for the given decimal exponent + // distance. + ASSERT(PowersOfTenCache::kDecimalExponentDistance == 8); + switch (exponent) { + case 1: return DiyFp(UINT64_2PART_C(0xa0000000, 00000000), -60); + case 2: return DiyFp(UINT64_2PART_C(0xc8000000, 00000000), -57); + case 3: return DiyFp(UINT64_2PART_C(0xfa000000, 00000000), -54); + case 4: return DiyFp(UINT64_2PART_C(0x9c400000, 00000000), -50); + case 5: return DiyFp(UINT64_2PART_C(0xc3500000, 00000000), -47); + case 6: return DiyFp(UINT64_2PART_C(0xf4240000, 00000000), -44); + case 7: return DiyFp(UINT64_2PART_C(0x98968000, 00000000), -40); + default: + UNREACHABLE(); + } +} + +static bool DiyFpStrtod(Vector<const char> buffer, + int exponent, + double* result) { + DiyFp input; + int remaining_decimals; + ReadDiyFp(buffer, &input, &remaining_decimals); + + const int kDenominatorLog = 3; + const int kDenominator = 1 << kDenominatorLog; + // Move the remaining decimals into the exponent. + exponent += remaining_decimals; + int error = (remaining_decimals == 0 ? 0 : kDenominator / 2); + + int old_e = input.e(); + input.Normalize(); + error <<= old_e - input.e(); + + ASSERT(exponent <= PowersOfTenCache::kMaxDecimalExponent); + if (exponent < PowersOfTenCache::kMinDecimalExponent) { + *result = 0.0; + return true; + } + DiyFp cached_power; + int cached_decimal_exponent; + PowersOfTenCache::GetCachedPowerForDecimalExponent(exponent, + &cached_power, + &cached_decimal_exponent); + + if (cached_decimal_exponent != exponent) { + int adjustment_exponent = exponent - cached_decimal_exponent; + DiyFp adjustment_power = AdjustmentPowerOfTen(adjustment_exponent); + input.Multiply(adjustment_power); + if (kMaxUint64DecimalDigits - buffer.length() >= adjustment_exponent) { + // The product of input with the adjustment power fits into a 64 bit + // integer. + ASSERT(DiyFp::kSignificandSize == 64); + } else { + // The adjustment power is exact. There is hence only an error of 0.5. + error += kDenominator / 2; + } + } + + input.Multiply(cached_power); + + int error_b = kDenominator / 2; + int error_ab = (error == 0 ? 0 : 1); // We round up to 1. + int fixed_error = kDenominator / 2; + error += error_b + error_ab + fixed_error; + + old_e = input.e(); + input.Normalize(); + error <<= old_e - input.e(); + + int order_of_magnitude = DiyFp::kSignificandSize + input.e(); + int effective_significand_size = + Double::SignificandSizeForOrderOfMagnitude(order_of_magnitude); + int precision_digits_count = + DiyFp::kSignificandSize - effective_significand_size; + if (precision_digits_count + kDenominatorLog >= DiyFp::kSignificandSize) { + int shift_amount = (precision_digits_count + kDenominatorLog) - + DiyFp::kSignificandSize + 1; + input.set_f(input.f() >> shift_amount); + input.set_e(input.e() + shift_amount); + error = (error >> shift_amount) + 1 + kDenominator; + precision_digits_count -= shift_amount; + } + + ASSERT(DiyFp::kSignificandSize == 64); + ASSERT(precision_digits_count < 64); + uint64_t one64 = 1; + uint64_t precision_bits_mask = (one64 << precision_digits_count) - 1; + uint64_t precision_bits = input.f() & precision_bits_mask; + uint64_t half_way = one64 << (precision_digits_count - 1); + precision_bits *= kDenominator; + half_way *= kDenominator; + DiyFp rounded_input(input.f() >> precision_digits_count, + input.e() + precision_digits_count); + if (precision_bits >= half_way + error) { + rounded_input.set_f(rounded_input.f() + 1); + } + + *result = Double(rounded_input).value(); + if (half_way - error < precision_bits && precision_bits < half_way + error) { + return false; + } else { + return true; + } +} + +static int CompareBufferWithDiyFp(Vector<const char> buffer, + int exponent, + DiyFp diy_fp) { + ASSERT(buffer.length() + exponent <= kMaxDecimalPower + 1); + ASSERT(buffer.length() + exponent > kMinDecimalPower); + ASSERT(buffer.length() <= kMaxSignificantDecimalDigits); + ASSERT(((kMaxDecimalPower + 1) * 333 / 100) < Bignum::kMaxSignificantBits); + + Bignum buffer_bignum; + Bignum diy_fp_bignum; + buffer_bignum.AssignDecimalString(buffer); + diy_fp_bignum.AssignUInt64(diy_fp.f()); + if (exponent >= 0) { + buffer_bignum.MultiplyByPowerOfTen(exponent); + } else { + diy_fp_bignum.MultiplyByPowerOfTen(-exponent); + } + if (diy_fp.e() > 0) { + diy_fp_bignum.ShiftLeft(diy_fp.e()); + } else { + buffer_bignum.ShiftLeft(-diy_fp.e()); + } + return Bignum::Compare(buffer_bignum, diy_fp_bignum); +} + +static bool ComputeGuess(Vector<const char> trimmed, int exponent, + double* guess) +{ + if (trimmed.length() == 0) { + *guess = 0.0; + return true; + } + if (exponent + trimmed.length() - 1 >= kMaxDecimalPower) { + *guess = Double::Infinity(); + return true; + } + if (exponent + trimmed.length() <= kMinDecimalPower) { + *guess = 0.0; + return true; + } + + if (DiyFpStrtod(trimmed, exponent, guess)) { + return true; + } + if (*guess == Double::Infinity()) { + return true; + } + return false; +} + +double Strtod(Vector<const char> buffer, int exponent) +{ + char copy_buffer[kMaxSignificantDecimalDigits]; + Vector<const char> trimmed; + int updated_exponent; + TrimAndCut(buffer, exponent, copy_buffer, kMaxSignificantDecimalDigits, + &trimmed, &updated_exponent); + exponent = updated_exponent; + + double guess; + bool is_correct = ComputeGuess(trimmed, exponent, &guess); + if (is_correct) return guess; + + DiyFp upper_boundary = Double(guess).UpperBoundary(); + int comparison = CompareBufferWithDiyFp(trimmed, exponent, upper_boundary); + if (comparison < 0) { + return guess; + } else if (comparison > 0) { + return Double(guess).NextDouble(); + } else if ((Double(guess).Significand() & 1) == 0) { + // Round towards even. + return guess; + } else { + return Double(guess).NextDouble(); + } +} + +class DoubleToStringConverter { +public: + static const int kMaxFixedDigitsBeforePoint = 60; + static const int kMaxFixedDigitsAfterPoint = 60; + static const int kMaxExponentialDigits = 120; + static const int kMinPrecisionDigits = 1; + static const int kMaxPrecisionDigits = 120; + + enum Flags { + NO_FLAGS = 0, + EMIT_POSITIVE_EXPONENT_SIGN = 1, + EMIT_TRAILING_DECIMAL_POINT = 2, + EMIT_TRAILING_ZERO_AFTER_POINT = 4, + UNIQUE_ZERO = 8 + }; + + DoubleToStringConverter(int flags, + const char* infinity_symbol, + const char* nan_symbol, + char exponent_character, + int decimal_in_shortest_low, + int decimal_in_shortest_high, + int max_leading_padding_zeroes_in_precision_mode, + int max_trailing_padding_zeroes_in_precision_mode) + : flags_(flags), + infinity_symbol_(infinity_symbol), + nan_symbol_(nan_symbol), + exponent_character_(exponent_character), + decimal_in_shortest_low_(decimal_in_shortest_low), + decimal_in_shortest_high_(decimal_in_shortest_high), + max_leading_padding_zeroes_in_precision_mode_( + max_leading_padding_zeroes_in_precision_mode), + max_trailing_padding_zeroes_in_precision_mode_( + max_trailing_padding_zeroes_in_precision_mode) { + // When 'trailing zero after the point' is set, then 'trailing point' + // must be set too. + ASSERT(((flags & EMIT_TRAILING_DECIMAL_POINT) != 0) || + !((flags & EMIT_TRAILING_ZERO_AFTER_POINT) != 0)); + } + + bool ToShortest(double value, std::string &s) const { + return ToShortestIeeeNumber(value, s, SHORTEST); + } + + bool ToFixed(double value, + int requested_digits, + std::string &s) const; + + bool ToExponential(double value, + int requested_digits, + std::string &s) const; + + bool ToPrecision(double value, + int precision, + std::string &s) const; + + enum DtoaMode { + SHORTEST, + FIXED, // Produce a fixed number of digits after the decimal point + PRECISION // Fixed number of digits (independent of the decimal point) + }; + + static const int kBase10MaximalLength = 17; + + // The result should be interpreted as buffer * 10^(point-length). + static void DoubleToAscii(double v, + DtoaMode mode, + int requested_digits, + char* buffer, + int buffer_length, + bool* sign, + int* length, + int* point); + + private: + // Implementation for ToShortest. + bool ToShortestIeeeNumber(double value, + std::string &s, + DtoaMode mode) const; + + bool HandleSpecialValues(double value, std::string &s) const; + + void CreateExponentialRepresentation(const char* decimal_digits, + int length, + int exponent, + std::string &s) const; + + void CreateDecimalRepresentation(const char* decimal_digits, + int length, + int decimal_point, + int digits_after_point, + std::string &s) const; + + const int flags_; + const char* const infinity_symbol_; + const char* const nan_symbol_; + const char exponent_character_; + const int decimal_in_shortest_low_; + const int decimal_in_shortest_high_; + const int max_leading_padding_zeroes_in_precision_mode_; + const int max_trailing_padding_zeroes_in_precision_mode_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(DoubleToStringConverter); +}; + + +class StringToDoubleConverter { + public: + enum Flags { + NO_FLAGS = 0, + ALLOW_HEX = 1, + ALLOW_OCTALS = 2, + ALLOW_TRAILING_JUNK = 4, + ALLOW_LEADING_SPACES = 8, + ALLOW_TRAILING_SPACES = 16, + ALLOW_SPACES_AFTER_SIGN = 32 + }; + + StringToDoubleConverter(int flags, + double empty_string_value, + double junk_string_value, + const char* infinity_symbol, + const char* nan_symbol) + : flags_(flags), + empty_string_value_(empty_string_value), + junk_string_value_(junk_string_value), + infinity_symbol_(infinity_symbol), + nan_symbol_(nan_symbol) { + } + + double StringToDouble(const char* buffer, + int length, + int* processed_characters_count) const; + + private: + const int flags_; + const double empty_string_value_; + const double junk_string_value_; + const char* const infinity_symbol_; + const char* const nan_symbol_; + + double StringToIeee(const char *start_pointer, + int length, + int* processed_characters_count) const; + + DISALLOW_IMPLICIT_CONSTRUCTORS(StringToDoubleConverter); +}; + +bool DoubleToStringConverter::HandleSpecialValues( + double value, + std::string &result) const { + Double double_inspect(value); + if (double_inspect.IsInfinite()) { + if (infinity_symbol_ == NULL) return false; + if (value < 0) { + result += '-'; + } + result += infinity_symbol_; + return true; + } + if (double_inspect.IsNan()) { + if (nan_symbol_ == NULL) return false; + result = nan_symbol_; + return true; + } + return false; +} + + +void DoubleToStringConverter::CreateExponentialRepresentation( + const char* decimal_digits, + int length, + int exponent, + std::string &result) const { + ASSERT(length != 0); + result += decimal_digits[0]; + if (length != 1) { + result += '.'; + result.append(&decimal_digits[1], length-1); + } + result += exponent_character_; + if (exponent < 0) { + result += '-'; + exponent = -exponent; + } else { + if ((flags_ & EMIT_POSITIVE_EXPONENT_SIGN) != 0) { + result += '+'; + } + } + if (exponent == 0) { + result += '0'; + return; + } + ASSERT(exponent < 1e4); + const int kMaxExponentLength = 5; + char buffer[kMaxExponentLength + 1]; + buffer[kMaxExponentLength] = '\0'; + int first_char_pos = kMaxExponentLength; + while (exponent > 0) { + buffer[--first_char_pos] = '0' + (exponent % 10); + exponent /= 10; + } + result.append(&buffer[first_char_pos], + kMaxExponentLength - first_char_pos); +} + + +void DoubleToStringConverter::CreateDecimalRepresentation( + const char* decimal_digits, + int length, + int decimal_point, + int digits_after_point, + std::string &result) const { + // Create a representation that is padded with zeros if needed. + if (decimal_point <= 0) { + // "0.00000decimal_rep". + result += '0'; + if (digits_after_point > 0) { + result += '.'; + result.append(-decimal_point, '0'); + ASSERT(length <= digits_after_point - (-decimal_point)); + result.append(decimal_digits, length); + int remaining_digits = digits_after_point - (-decimal_point) - length; + result.append(remaining_digits, '0'); + } + } else if (decimal_point >= length) { + // "decimal_rep0000.00000" or "decimal_rep.0000" + result.append(decimal_digits, length); + result.append(decimal_point - length, '0'); + if (digits_after_point > 0) { + result += '.'; + result.append(digits_after_point, '0'); + } + } else { + // "decima.l_rep000" + ASSERT(digits_after_point > 0); + result.append(decimal_digits, decimal_point); + result += '.'; + ASSERT(length - decimal_point <= digits_after_point); + result.append(&decimal_digits[decimal_point], length - decimal_point); + int remaining_digits = digits_after_point - (length - decimal_point); + result.append(remaining_digits, '0'); + } + if (digits_after_point == 0) { + if ((flags_ & EMIT_TRAILING_DECIMAL_POINT) != 0) { + result += '.'; + } + if ((flags_ & EMIT_TRAILING_ZERO_AFTER_POINT) != 0) { + result += '0'; + } + } +} + + +bool DoubleToStringConverter::ToShortestIeeeNumber( + double value, + std::string &result, + DoubleToStringConverter::DtoaMode mode) const { + ASSERT(mode == SHORTEST); + if (Double(value).IsSpecial()) { + return HandleSpecialValues(value, result); + } + + int decimal_point; + bool sign; + const int kDecimalRepCapacity = kBase10MaximalLength + 1; + char decimal_rep[kDecimalRepCapacity]; + int decimal_rep_length; + + DoubleToAscii(value, mode, 0, decimal_rep, kDecimalRepCapacity, + &sign, &decimal_rep_length, &decimal_point); + + bool unique_zero = (flags_ & UNIQUE_ZERO) != 0; + if (sign && (value != 0.0 || !unique_zero)) { + result += '-'; + } + + int exponent = decimal_point - 1; + if ((decimal_in_shortest_low_ <= exponent) && + (exponent < decimal_in_shortest_high_)) { + CreateDecimalRepresentation(decimal_rep, decimal_rep_length, decimal_point, + std::max(0, decimal_rep_length - decimal_point), + result); + } else { + CreateExponentialRepresentation(decimal_rep, decimal_rep_length, exponent, result); + } + return true; +} + + +bool DoubleToStringConverter::ToFixed(double value, + int requested_digits, + std::string &result) const +{ + ASSERT(kMaxFixedDigitsBeforePoint == 60); + const double kFirstNonFixed = 1e60; + + if (Double(value).IsSpecial()) { + return HandleSpecialValues(value, result); + } + + if (requested_digits > kMaxFixedDigitsAfterPoint) return false; + if (value >= kFirstNonFixed || value <= -kFirstNonFixed) return false; + + // Find a sufficiently precise decimal representation of n. + int decimal_point; + bool sign; + // Add space for the '\0' byte. + const int kDecimalRepCapacity = + kMaxFixedDigitsBeforePoint + kMaxFixedDigitsAfterPoint + 1; + char decimal_rep[kDecimalRepCapacity]; + int decimal_rep_length; + DoubleToAscii(value, FIXED, requested_digits, + decimal_rep, kDecimalRepCapacity, + &sign, &decimal_rep_length, &decimal_point); + + bool unique_zero = ((flags_ & UNIQUE_ZERO) != 0); + if (sign && (value != 0.0 || !unique_zero)) { + result += '-'; + } + + CreateDecimalRepresentation(decimal_rep, decimal_rep_length, decimal_point, + requested_digits, result); + return true; +} + + +bool DoubleToStringConverter::ToExponential( + double value, + int requested_digits, + std::string &result) const { + if (Double(value).IsSpecial()) { + return HandleSpecialValues(value, result); + } + + if (requested_digits < -1) return false; + if (requested_digits > kMaxExponentialDigits) return false; + + int decimal_point; + bool sign; + // Add space for digit before the decimal point and the '\0' character. + const int kDecimalRepCapacity = kMaxExponentialDigits + 2; + ASSERT(kDecimalRepCapacity > kBase10MaximalLength); + char decimal_rep[kDecimalRepCapacity]; + int decimal_rep_length; + + if (requested_digits == -1) { + DoubleToAscii(value, SHORTEST, 0, + decimal_rep, kDecimalRepCapacity, + &sign, &decimal_rep_length, &decimal_point); + } else { + DoubleToAscii(value, PRECISION, requested_digits + 1, + decimal_rep, kDecimalRepCapacity, + &sign, &decimal_rep_length, &decimal_point); + ASSERT(decimal_rep_length <= requested_digits + 1); + + for (int i = decimal_rep_length; i < requested_digits + 1; ++i) { + decimal_rep[i] = '0'; + } + decimal_rep_length = requested_digits + 1; + } + + bool unique_zero = ((flags_ & UNIQUE_ZERO) != 0); + if (sign && (value != 0.0 || !unique_zero)) { + result += '-'; + } + + int exponent = decimal_point - 1; + CreateExponentialRepresentation(decimal_rep, + decimal_rep_length, + exponent, result); + return true; +} + + +bool DoubleToStringConverter::ToPrecision(double value, + int precision, + std::string &result) const { + if (Double(value).IsSpecial()) { + return HandleSpecialValues(value, result); + } + + if (precision < kMinPrecisionDigits || precision > kMaxPrecisionDigits) { + return false; + } + + // Find a sufficiently precise decimal representation of n. + int decimal_point; + bool sign; + // Add one for the terminating null character. + const int kDecimalRepCapacity = kMaxPrecisionDigits + 1; + char decimal_rep[kDecimalRepCapacity]; + int decimal_rep_length; + + DoubleToAscii(value, PRECISION, precision, + decimal_rep, kDecimalRepCapacity, + &sign, &decimal_rep_length, &decimal_point); + ASSERT(decimal_rep_length <= precision); + + bool unique_zero = ((flags_ & UNIQUE_ZERO) != 0); + if (sign && (value != 0.0 || !unique_zero)) { + result += '-'; + } + + // The exponent if we print the number as x.xxeyyy. That is with the + // decimal point after the first digit. + int exponent = decimal_point - 1; + + int extra_zero = ((flags_ & EMIT_TRAILING_ZERO_AFTER_POINT) != 0) ? 1 : 0; + if ((-decimal_point + 1 > max_leading_padding_zeroes_in_precision_mode_) || + (decimal_point - precision + extra_zero > + max_trailing_padding_zeroes_in_precision_mode_)) { + for (int i = decimal_rep_length; i < precision; ++i) { + decimal_rep[i] = '0'; + } + + CreateExponentialRepresentation(decimal_rep, + precision, + exponent, + result); + } else { + CreateDecimalRepresentation(decimal_rep, decimal_rep_length, decimal_point, + std::max(0, precision - decimal_point), + result); + } + return true; +} + + +static BignumDtoaMode DtoaToBignumDtoaMode( + DoubleToStringConverter::DtoaMode dtoa_mode) { + switch (dtoa_mode) { + case DoubleToStringConverter::SHORTEST: return BIGNUM_DTOA_SHORTEST; + case DoubleToStringConverter::FIXED: return BIGNUM_DTOA_FIXED; + case DoubleToStringConverter::PRECISION: return BIGNUM_DTOA_PRECISION; + default: + UNREACHABLE(); + } +} + + +void DoubleToStringConverter::DoubleToAscii(double v, DtoaMode mode, int requested_digits, + char* buffer, int buffer_length, + bool* sign, int* length, int* point) +{ + Vector<char> vector(buffer, buffer_length); + ASSERT(!Double(v).IsSpecial()); + ASSERT(mode == SHORTEST || requested_digits >= 0); + + if (Double(v).Sign() < 0) { + *sign = true; + v = -v; + } else { + *sign = false; + } + + if (mode == PRECISION && requested_digits == 0) { + vector[0] = '\0'; + *length = 0; + return; + } + + if (v == 0) { + vector[0] = '0'; + vector[1] = '\0'; + *length = 1; + *point = 1; + return; + } + + bool fast_worked; + switch (mode) { + case SHORTEST: + fast_worked = FastDtoa(v, FAST_DTOA_SHORTEST, 0, vector, length, point); + break; + case FIXED: + fast_worked = FastFixedDtoa(v, requested_digits, vector, length, point); + break; + case PRECISION: + fast_worked = FastDtoa(v, FAST_DTOA_PRECISION, requested_digits, + vector, length, point); + break; + default: + fast_worked = false; + UNREACHABLE(); + } + if (fast_worked) return; + + // If the fast dtoa didn't succeed use the slower bignum version. + BignumDtoaMode bignum_mode = DtoaToBignumDtoaMode(mode); + BignumDtoa(v, bignum_mode, requested_digits, vector, length, point); + vector[*length] = '\0'; +} + +template <class Iterator> +static bool ConsumeSubString(Iterator* current, + Iterator end, + const char* substring) { + ASSERT(**current == *substring); + for (substring++; *substring != '\0'; substring++) { + ++*current; + if (*current == end || **current != *substring) return false; + } + ++*current; + return true; +} + +const int kMaxSignificantDigits = 772; + +static const char kWhitespaceTable7[] = { 32, 13, 10, 9, 11, 12 }; +static const int kWhitespaceTable7Length = ARRAY_SIZE(kWhitespaceTable7); + +static bool isWhitespace(int x) { + if (x < 128) { + for (int i = 0; i < kWhitespaceTable7Length; i++) { + if (kWhitespaceTable7[i] == x) return true; + } + } + return false; +} + +// Returns true if a nonspace found and false if the end has reached. +template <class Iterator> +static inline bool AdvanceToNonspace(Iterator* current, Iterator end) { + while (*current != end) { + if (!isWhitespace(**current)) return true; + ++*current; + } + return false; +} + +static bool isDigit(int x, int radix) { + return (x >= '0' && x <= '9' && x < '0' + radix) + || (radix > 10 && x >= 'a' && x < 'a' + radix - 10) + || (radix > 10 && x >= 'A' && x < 'A' + radix - 10); +} + +static double SignedZero(bool sign) { + return sign ? -0.0 : 0.0; +} + +static bool IsDecimalDigitForRadix(int c, int radix) { + return '0' <= c && c <= '9' && (c - '0') < radix; +} + +static bool IsCharacterDigitForRadix(int c, int radix, char a_character) { + return radix > 10 && c >= a_character && c < a_character + radix - 10; +} + +template <int radix_log_2, class Iterator> +static double RadixStringToIeee(Iterator* current, Iterator end, + bool sign, bool allow_trailing_junk, double junk_string_value, + bool* result_is_junk) +{ + ASSERT(*current != end); + + const int kSignificandSize = Double::kSignificandSize; + + *result_is_junk = true; + + // Skip leading 0s. + while (**current == '0') { + ++(*current); + if (*current == end) { + *result_is_junk = false; + return SignedZero(sign); + } + } + + int64_t number = 0; + int exponent = 0; + const int radix = (1 << radix_log_2); + + do { + int digit; + if (IsDecimalDigitForRadix(**current, radix)) { + digit = static_cast<char>(**current) - '0'; + } else if (IsCharacterDigitForRadix(**current, radix, 'a')) { + digit = static_cast<char>(**current) - 'a' + 10; + } else if (IsCharacterDigitForRadix(**current, radix, 'A')) { + digit = static_cast<char>(**current) - 'A' + 10; + } else { + if (allow_trailing_junk || !AdvanceToNonspace(current, end)) { + break; + } else { + return junk_string_value; + } + } + + number = number * radix + digit; + int overflow = static_cast<int>(number >> kSignificandSize); + if (overflow != 0) { + // Overflow occurred. Need to determine which direction to round the + // result. + int overflow_bits_count = 1; + while (overflow > 1) { + overflow_bits_count++; + overflow >>= 1; + } + + int dropped_bits_mask = ((1 << overflow_bits_count) - 1); + int dropped_bits = static_cast<int>(number) & dropped_bits_mask; + number >>= overflow_bits_count; + exponent = overflow_bits_count; + + bool zero_tail = true; + for (;;) { + ++(*current); + if (*current == end || !isDigit(**current, radix)) break; + zero_tail = zero_tail && **current == '0'; + exponent += radix_log_2; + } + + if (!allow_trailing_junk && AdvanceToNonspace(current, end)) { + return junk_string_value; + } + + int middle_value = (1 << (overflow_bits_count - 1)); + if (dropped_bits > middle_value) { + number++; // Rounding up. + } else if (dropped_bits == middle_value) { + // Rounding to even to consistency with decimals: half-way case rounds + // up if significant part is odd and down otherwise. + if ((number & 1) != 0 || !zero_tail) { + number++; // Rounding up. + } + } + + // Rounding up may cause overflow. + if ((number & ((int64_t)1 << kSignificandSize)) != 0) { + exponent++; + number >>= 1; + } + break; + } + ++(*current); + } while (*current != end); + + ASSERT(number < ((int64_t)1 << kSignificandSize)); + ASSERT(static_cast<int64_t>(static_cast<double>(number)) == number); + + *result_is_junk = false; + + if (exponent == 0) { + if (sign) { + if (number == 0) return -0.0; + number = -number; + } + return static_cast<double>(number); + } + + ASSERT(number != 0); + return Double(DiyFp(number, exponent)).value(); +} + +double StringToDoubleConverter::StringToIeee( + const char *input, + int length, + int* processed_characters_count) const { + const char *current = input; + const char *end = input + length; + + *processed_characters_count = 0; + + const bool allow_trailing_junk = (flags_ & ALLOW_TRAILING_JUNK) != 0; + const bool allow_leading_spaces = (flags_ & ALLOW_LEADING_SPACES) != 0; + const bool allow_trailing_spaces = (flags_ & ALLOW_TRAILING_SPACES) != 0; + const bool allow_spaces_after_sign = (flags_ & ALLOW_SPACES_AFTER_SIGN) != 0; + + if (current == end) return empty_string_value_; + + if (allow_leading_spaces || allow_trailing_spaces) { + if (!AdvanceToNonspace(¤t, end)) { + *processed_characters_count = static_cast<int>(current - input); + return empty_string_value_; + } + if (!allow_leading_spaces && (input != current)) { + return junk_string_value_; + } + } + + const int kBufferSize = kMaxSignificantDigits + 10; + char buffer[kBufferSize]; // NOLINT: size is known at compile time. + int buffer_pos = 0; + + int exponent = 0; + int significant_digits = 0; + int insignificant_digits = 0; + bool nonzero_digit_dropped = false; + + bool sign = false; + + if (*current == '+' || *current == '-') { + sign = (*current == '-'); + ++current; + const char *next_non_space = current; + + if (!AdvanceToNonspace(&next_non_space, end)) return junk_string_value_; + if (!allow_spaces_after_sign && (current != next_non_space)) { + return junk_string_value_; + } + current = next_non_space; + } + + if (infinity_symbol_ != NULL) { + if (*current == infinity_symbol_[0]) { + if (!ConsumeSubString(¤t, end, infinity_symbol_)) { + return junk_string_value_; + } + + if (!(allow_trailing_spaces || allow_trailing_junk) && (current != end)) { + return junk_string_value_; + } + if (!allow_trailing_junk && AdvanceToNonspace(¤t, end)) { + return junk_string_value_; + } + + ASSERT(buffer_pos == 0); + *processed_characters_count = static_cast<int>(current - input); + return sign ? -Double::Infinity() : Double::Infinity(); + } + } + + if (nan_symbol_ != NULL) { + if (*current == nan_symbol_[0]) { + if (!ConsumeSubString(¤t, end, nan_symbol_)) { + return junk_string_value_; + } + + if (!(allow_trailing_spaces || allow_trailing_junk) && (current != end)) { + return junk_string_value_; + } + if (!allow_trailing_junk && AdvanceToNonspace(¤t, end)) { + return junk_string_value_; + } + + ASSERT(buffer_pos == 0); + *processed_characters_count = static_cast<int>(current - input); + return sign ? -Double::NaN() : Double::NaN(); + } + } + + bool leading_zero = false; + if (*current == '0') { + ++current; + if (current == end) { + *processed_characters_count = static_cast<int>(current - input); + return SignedZero(sign); + } + + leading_zero = true; + + // It could be hexadecimal value. + if ((flags_ & ALLOW_HEX) && (*current == 'x' || *current == 'X')) { + ++current; + if (current == end || !isDigit(*current, 16)) { + return junk_string_value_; // "0x". + } + + bool result_is_junk; + double result = RadixStringToIeee<4>(¤t, + end, + sign, + allow_trailing_junk, + junk_string_value_, + &result_is_junk); + if (!result_is_junk) { + if (allow_trailing_spaces) AdvanceToNonspace(¤t, end); + *processed_characters_count = static_cast<int>(current - input); + } + return result; + } + + // Ignore leading zeros in the integer part. + while (*current == '0') { + ++current; + if (current == end) { + *processed_characters_count = static_cast<int>(current - input); + return SignedZero(sign); + } + } + } + + bool octal = leading_zero && (flags_ & ALLOW_OCTALS) != 0; + + // Copy significant digits of the integer part (if any) to the buffer. + while (*current >= '0' && *current <= '9') { + if (significant_digits < kMaxSignificantDigits) { + ASSERT(buffer_pos < kBufferSize); + buffer[buffer_pos++] = static_cast<char>(*current); + significant_digits++; + // Will later check if it's an octal in the buffer. + } else { + insignificant_digits++; // Move the digit into the exponential part. + nonzero_digit_dropped = nonzero_digit_dropped || *current != '0'; + } + octal = octal && *current < '8'; + ++current; + if (current == end) goto parsing_done; + } + + if (significant_digits == 0) { + octal = false; + } + + if (*current == '.') { + if (octal && !allow_trailing_junk) return junk_string_value_; + if (octal) goto parsing_done; + + ++current; + if (current == end) { + if (significant_digits == 0 && !leading_zero) { + return junk_string_value_; + } else { + goto parsing_done; + } + } + + if (significant_digits == 0) { + // octal = false; + // Integer part consists of 0 or is absent. Significant digits start after + // leading zeros (if any). + while (*current == '0') { + ++current; + if (current == end) { + *processed_characters_count = static_cast<int>(current - input); + return SignedZero(sign); + } + exponent--; // Move this 0 into the exponent. + } + } + + // There is a fractional part. + // We don't emit a '.', but adjust the exponent instead. + while (*current >= '0' && *current <= '9') { + if (significant_digits < kMaxSignificantDigits) { + ASSERT(buffer_pos < kBufferSize); + buffer[buffer_pos++] = static_cast<char>(*current); + significant_digits++; + exponent--; + } else { + // Ignore insignificant digits in the fractional part. + nonzero_digit_dropped = nonzero_digit_dropped || *current != '0'; + } + ++current; + if (current == end) goto parsing_done; + } + } + + if (!leading_zero && exponent == 0 && significant_digits == 0) { + // If leading_zeros is true then the string contains zeros. + // If exponent < 0 then string was [+-]\.0*... + // If significant_digits != 0 the string is not equal to 0. + // Otherwise there are no digits in the string. + return junk_string_value_; + } + + // Parse exponential part. + if (*current == 'e' || *current == 'E') { + if (octal && !allow_trailing_junk) return junk_string_value_; + if (octal) goto parsing_done; + ++current; + if (current == end) { + if (allow_trailing_junk) { + goto parsing_done; + } else { + return junk_string_value_; + } + } + char sign = '+'; + if (*current == '+' || *current == '-') { + sign = static_cast<char>(*current); + ++current; + if (current == end) { + if (allow_trailing_junk) { + goto parsing_done; + } else { + return junk_string_value_; + } + } + } + + if (current == end || *current < '0' || *current > '9') { + if (allow_trailing_junk) { + goto parsing_done; + } else { + return junk_string_value_; + } + } + + const int max_exponent = INT_MAX / 2; + ASSERT(-max_exponent / 2 <= exponent && exponent <= max_exponent / 2); + int num = 0; + do { + // Check overflow. + int digit = *current - '0'; + if (num >= max_exponent / 10 + && !(num == max_exponent / 10 && digit <= max_exponent % 10)) { + num = max_exponent; + } else { + num = num * 10 + digit; + } + ++current; + } while (current != end && *current >= '0' && *current <= '9'); + + exponent += (sign == '-' ? -num : num); + } + + if (!(allow_trailing_spaces || allow_trailing_junk) && (current != end)) { + return junk_string_value_; + } + if (!allow_trailing_junk && AdvanceToNonspace(¤t, end)) { + return junk_string_value_; + } + if (allow_trailing_spaces) { + AdvanceToNonspace(¤t, end); + } + + parsing_done: + exponent += insignificant_digits; + + if (octal) { + double result; + bool result_is_junk; + char* start = buffer; + result = RadixStringToIeee<3>(&start, + buffer + buffer_pos, + sign, + allow_trailing_junk, + junk_string_value_, + &result_is_junk); + ASSERT(!result_is_junk); + *processed_characters_count = static_cast<int>(current - input); + return result; + } + + if (nonzero_digit_dropped) { + buffer[buffer_pos++] = '1'; + exponent--; + } + + ASSERT(buffer_pos < kBufferSize); + buffer[buffer_pos] = '\0'; + + double converted = Strtod(Vector<const char>(buffer, buffer_pos), exponent); + *processed_characters_count = static_cast<int>(current - input); + return sign? -converted: converted; +} + + +double StringToDoubleConverter::StringToDouble( + const char* buffer, + int length, + int* processed_characters_count) const { + return StringToIeee(buffer, length, processed_characters_count); +} + +} // end anonymous namespace + +std::string format_coord_shortest(Coord x) +{ + char buf[20]; + bool sign; + int length, point; + + DoubleToStringConverter::DoubleToAscii(x, DoubleToStringConverter::SHORTEST, + 0, buf, 20, &sign, &length, &point); + + int exponent = point - length; + + std::string ret; + ret.reserve(32); + + if (sign) { + ret += '-'; + } + + if (exponent == 0) { + // return digits without any changes + ret += buf; + } else if (point >= 0 && point <= length) { + // insert decimal point + ret.append(buf, point); + ret += '.'; + ret.append(&buf[point], length - point); + } else if (exponent > 0 && exponent <= 2) { + // add trailing zeroes + ret += buf; + ret.append(exponent, '0'); + } else if (point >= -3 && point <= -1) { + // add leading zeroes + ret += '.'; + ret.append(-point, '0'); + ret += buf; + } else { + // exponential form + ret += buf; + ret += 'e'; + if (exponent < 0) { + ret += '-'; + exponent = -exponent; + } + + /* Convert exponent by hand. + * Using ostringstream is ~3x slower */ + int const buflen = 6; + int i = 0; + char expdigits[buflen+1]; + expdigits[buflen] = 0; + + for (; exponent && i < buflen; ++i) { + expdigits[buflen - 1 - i] = '0' + (exponent % 10); + exponent /= 10; + } + ret.append(&expdigits[buflen - i]); + } + + return ret; +} + +std::string format_coord_nice(Coord x) +{ + static DoubleToStringConverter conv( + DoubleToStringConverter::UNIQUE_ZERO, + "inf", "NaN", 'e', -6, 21, 0, 0); + std::string ret; + ret.reserve(32); + conv.ToShortest(x, ret); + return ret; +} + +Coord parse_coord(std::string const &s) +{ + static StringToDoubleConverter conv( + StringToDoubleConverter::ALLOW_LEADING_SPACES | + StringToDoubleConverter::ALLOW_TRAILING_SPACES | + StringToDoubleConverter::ALLOW_SPACES_AFTER_SIGN, + 0.0, nan(""), "inf", "NaN"); + int dummy; + return conv.StringToDouble(s.c_str(), s.length(), &dummy); +} + +} // namespace Geom + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/2geom/coord.h b/src/2geom/coord.h index 78a852f32..9cc220db7 100644 --- a/src/2geom/coord.h +++ b/src/2geom/coord.h @@ -1,8 +1,10 @@ -/** - * \file - * \brief Defines the Coord "real" type with sufficient precision for coordinates. +/** @file + * @brief Integral and real coordinate types and some basic utilities *//* - * Copyright 2006 Nathan Hurst <njh@mail.csse.monash.edu.au> + * Authors: + * Nathan Hurst <njh@mail.csse.monash.edu.au> + * Krzysztof Kosiński <tweenk.pl@gmail.com> + * Copyright 2006-2015 Authors * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public @@ -34,34 +36,92 @@ #include <cmath> #include <limits> +#include <string> +#include <functional> #include <boost/operators.hpp> #include <2geom/forward.h> namespace Geom { -/** @brief 2D axis enumeration (X or Y). */ +/** @brief 2D axis enumeration (X or Y). + * @ingroup Primitives */ enum Dim2 { X=0, Y=1 }; -/** - * @brief Floating point type used to store coordinates. - * - * You may safely assume that double (or even float) provides enough precision for storing - * on-canvas points, and hence that double provides enough precision for dot products of - * differences of on-canvas points. - */ +/** @brief Get the other (perpendicular) dimension. + * @ingroup Primitives */ +inline Dim2 other_dimension(Dim2 d) { return d == Y ? X : Y; } + +// TODO: make a smarter implementation with C++11 +template <typename T> +struct D2Traits { + typedef typename T::D1Value D1Value; + typedef typename T::D1Reference D1Reference; + typedef typename T::D1ConstReference D1ConstReference; +}; + +/** @brief Axis extraction functor. + * For use with things such as Boost's transform_iterator. + * @ingroup Utilities */ +template <Dim2 D, typename T> +struct GetAxis { + typedef typename D2Traits<T>::D1Value result_type; + typedef T argument_type; + typename D2Traits<T>::D1Value operator()(T const &a) const { + return a[D]; + } +}; + +/** @brief Floating point type used to store coordinates. + * @ingroup Primitives */ typedef double Coord; + +/** @brief Type used for integral coordinates. + * @ingroup Primitives */ typedef int IntCoord; -const Coord EPSILON = 1e-5; //1e-18; +/** @brief Default "acceptably small" value. + * @ingroup Primitives */ +const Coord EPSILON = 1e-6; //1e-18; +/** @brief Get a value representing infinity. + * @ingroup Primitives */ inline Coord infinity() { return std::numeric_limits<Coord>::infinity(); } -//IMPL: NearConcept +/** @brief Nearness predicate for values. + * @ingroup Primitives */ inline bool are_near(Coord a, Coord b, double eps=EPSILON) { return a-b <= eps && a-b >= -eps; } inline bool rel_error_bound(Coord a, Coord b, double eps=EPSILON) { return a <= eps*b && a >= -eps*b; } +/** @brief Numerically stable linear interpolation. + * @ingroup Primitives */ +inline Coord lerp(Coord t, Coord a, Coord b) { + return (1 - t) * a + t * b; +} + +/** @brief Traits class used with coordinate types. + * Defines point, interval and rectangle types for the given coordinate type. + * @ingroup Utilities */ template <typename C> -struct CoordTraits {}; +struct CoordTraits { + typedef D2<C> PointType; + typedef GenericInterval<C> IntervalType; + typedef GenericOptInterval<C> OptIntervalType; + typedef GenericRect<C> RectType; + typedef GenericOptRect<C> OptRectType; + + typedef + boost::equality_comparable< IntervalType + , boost::orable< IntervalType + > > + IntervalOps; + + typedef + boost::equality_comparable< RectType + , boost::orable< RectType + , boost::orable< RectType, OptRectType + > > > + RectOps; +}; // NOTE: operator helpers for Rect and Interval are defined here. // This is to avoid increasing their size through multiple inheritance. @@ -120,6 +180,24 @@ struct CoordTraits<Coord> { RectOps; }; +/** @brief Convert coordinate to shortest possible string. + * @return The shortest string that parses back to the original value. + * @relates Coord */ +std::string format_coord_shortest(Coord x); + +/** @brief Convert coordinate to human-readable string. + * Unlike format_coord_shortest, this function will not omit a leading zero + * before a decimal point or use small negative exponents. The output format + * is similar to Javascript functions. + * @relates Coord */ +std::string format_coord_nice(Coord x); + +/** @brief Parse coordinate string. + * When using this function in conjunction with format_coord_shortest() + * or format_coord_nice(), the value is guaranteed to be preserved exactly. + * @relates Coord */ +Coord parse_coord(std::string const &s); + } // end namespace Geom #endif // LIB2GEOM_SEEN_COORD_H diff --git a/src/2geom/crossing.cpp b/src/2geom/crossing.cpp index 513327271..e27a2fc43 100644 --- a/src/2geom/crossing.cpp +++ b/src/2geom/crossing.cpp @@ -28,7 +28,7 @@ double wrap_dist(double from, double to, double size, bool rev) { } } /* -CrossingGraph create_crossing_graph(std::vector<Path> const &p, Crossings const &crs) { +CrossingGraph create_crossing_graph(PathVector const &p, Crossings const &crs) { std::vector<Point> locs; CrossingGraph ret; for(unsigned i = 0; i < crs.size(); i++) { diff --git a/src/2geom/crossing.h b/src/2geom/crossing.h index d5012ae2b..425fa58f5 100644 --- a/src/2geom/crossing.h +++ b/src/2geom/crossing.h @@ -33,14 +33,14 @@ * */ -#ifndef __GEOM_CROSSING_H -#define __GEOM_CROSSING_H +#ifndef LIB2GEOM_SEEN_CROSSING_H +#define LIB2GEOM_SEEN_CROSSING_H #include <vector> #include <2geom/rect.h> -#include <2geom/sweep.h> +#include <2geom/sweep-bounds.h> #include <boost/optional/optional.hpp> -#include <2geom/path.h> +#include <2geom/pathvector.h> namespace Geom { @@ -99,7 +99,7 @@ struct TimeOrder { }; class Path; -CrossingGraph create_crossing_graph(std::vector<Path> const &p, Crossings const &crs); +CrossingGraph create_crossing_graph(PathVector const &p, Crossings const &crs); */ /*inline bool are_near(Crossing a, Crossing b) { @@ -143,11 +143,24 @@ std::vector<Rect> bounds(Path const &a); inline void sort_crossings(Crossings &cr, unsigned ix) { std::sort(cr.begin(), cr.end(), CrossingOrder(ix)); } +template <typename T> +struct CrossingTraits { + typedef std::vector<T> VectorT; + static inline VectorT init(T const &x) { return VectorT(1, x); } +}; +template <> +struct CrossingTraits<Path> { + typedef PathVector VectorT; + static inline VectorT vector_one(Path const &x) { return VectorT(x); } +}; + template<typename T> struct Crosser { + typedef typename CrossingTraits<T>::VectorT VectorT; virtual ~Crosser() {} - virtual Crossings crossings(T const &a, T const &b) { return crossings(std::vector<T>(1,a), std::vector<T>(1,b))[0]; } - virtual CrossingSet crossings(std::vector<T> const &a, std::vector<T> const &b) { + virtual Crossings crossings(T const &a, T const &b) { + return crossings(CrossingTraits<T>::vector_one(a), CrossingTraits<T>::vector_one(b))[0]; } + virtual CrossingSet crossings(VectorT const &a, VectorT const &b) { CrossingSet results(a.size() + b.size(), Crossings()); std::vector<std::vector<unsigned> > cull = sweep_bounds(bounds(a), bounds(b)); @@ -185,7 +198,7 @@ CrossingSet reverse_tb(CrossingSet const &cr, unsigned split, std::vector<double void clean(Crossings &cr_a, Crossings &cr_b); void delete_duplicates(Crossings &crs); -} +} // end namespace Geom #endif /* diff --git a/src/2geom/curve.cpp b/src/2geom/curve.cpp index c0f2bf883..b45228514 100644 --- a/src/2geom/curve.cpp +++ b/src/2geom/curve.cpp @@ -32,62 +32,26 @@ */ #include <2geom/curve.h> -#include <2geom/nearest-point.h> +#include <2geom/exception.h> +#include <2geom/nearest-time.h> #include <2geom/sbasis-geometric.h> +#include <2geom/sbasis-to-bezier.h> #include <2geom/ord.h> +#include <2geom/path-sink.h> + +//#include <iostream> namespace Geom { -int CurveHelpers::root_winding(Curve const &c, Point p) { - std::vector<double> ts = c.roots(p[Y], Y); - - if(ts.empty()) return 0; - - double const fudge = 0.01; //fudge factor used on first and last - - std::sort(ts.begin(), ts.end()); - - // winding determined by crossings at roots - int wind=0; - // previous time - double pt = ts.front() - fudge; - for ( std::vector<double>::iterator ti = ts.begin() - ; ti != ts.end() - ; ++ti ) - { - double t = *ti; - if ( t <= 0. || t >= 1. ) continue; //skip endpoint roots - if ( c.valueAt(t, X) > p[X] ) { // root is ray intersection - // Get t of next: - std::vector<double>::iterator next = ti; - ++next; - double nt; - if(next == ts.end()) nt = t + fudge; else nt = *next; - - // Check before in time and after in time for positions - // Currently we're using the average times between next and previous segs - Cmp after_to_ray = cmp(c.valueAt((t + nt) / 2, Y), p[Y]); - Cmp before_to_ray = cmp(c.valueAt((t + pt) / 2, Y), p[Y]); - // if y is included, these will have opposite values, giving order. - Cmp dt = cmp(after_to_ray, before_to_ray); - if(dt != EQUAL_TO) //Should always be true, but yah never know.. - wind += dt; - pt = t; - } - } - - return wind; -} - -Coord Curve::nearestPoint(Point const& p, Coord a, Coord b) const +Coord Curve::nearestTime(Point const& p, Coord a, Coord b) const { - return nearest_point(p, toSBasis(), a, b); + return nearest_time(p, toSBasis(), a, b); } -std::vector<Coord> Curve::allNearestPoints(Point const& p, Coord from, Coord to) const +std::vector<Coord> Curve::allNearestTimes(Point const& p, Coord from, Coord to) const { - return all_nearest_points(p, toSBasis(), from, to); + return all_nearest_times(p, toSBasis(), from, to); } Coord Curve::length(Coord tolerance) const @@ -95,6 +59,97 @@ Coord Curve::length(Coord tolerance) const return ::Geom::length(toSBasis(), tolerance); } +int Curve::winding(Point const &p) const +{ + try { + std::vector<Coord> ts = roots(p[Y], Y); + if(ts.empty()) return 0; + std::sort(ts.begin(), ts.end()); + + // skip endpoint roots when they are local maxima on the Y axis + // this follows the convention used in other winding routines, + // i.e. that the bottommost coordinate is not part of the shape + bool ingore_0 = unitTangentAt(0)[Y] <= 0; + bool ignore_1 = unitTangentAt(1)[Y] >= 0; + + int wind = 0; + for (std::size_t i = 0; i < ts.size(); ++i) { + Coord t = ts[i]; + //std::cout << t << std::endl; + if ((t == 0 && ingore_0) || (t == 1 && ignore_1)) continue; + if (valueAt(t, X) > p[X]) { // root is ray intersection + Point tangent = unitTangentAt(t); + if (tangent[Y] > 0) { + // at the point of intersection, curve goes in +Y direction, + // so it winds in the direction of positive angles + ++wind; + } else if (tangent[Y] < 0) { + --wind; + } + } + } + return wind; + } catch (InfiniteSolutions const &e) { + // this means we encountered a line segment exactly coincident with the point + // skip, since this will be taken care of by endpoint roots in other segments + return 0; + } +} + +std::vector<CurveIntersection> Curve::intersect(Curve const &/*other*/, Coord /*eps*/) const +{ + // TODO: approximate as Bezier + THROW_NOTIMPLEMENTED(); +} + +std::vector<CurveIntersection> Curve::intersectSelf(Coord eps) const +{ + std::vector<CurveIntersection> result; + // Monotonic segments cannot have self-intersections. + // Thus, we can split the curve at roots and intersect the portions. + std::vector<Coord> splits; + std::auto_ptr<Curve> deriv(derivative()); + splits = deriv->roots(0, X); + if (splits.empty()) { + return result; + } + deriv.reset(); + splits.push_back(1.); + + boost::ptr_vector<Curve> parts; + Coord previous = 0; + for (unsigned i = 0; i < splits.size(); ++i) { + if (splits[i] == 0.) continue; + parts.push_back(portion(previous, splits[i])); + previous = splits[i]; + } + + Coord prev_i = 0; + for (unsigned i = 0; i < parts.size()-1; ++i) { + Interval dom_i(prev_i, splits[i]); + prev_i = splits[i]; + + Coord prev_j = 0; + for (unsigned j = i+1; j < parts.size(); ++j) { + Interval dom_j(prev_j, splits[j]); + prev_j = splits[j]; + + std::vector<CurveIntersection> xs = parts[i].intersect(parts[j], eps); + for (unsigned k = 0; k < xs.size(); ++k) { + // to avoid duplicated intersections, skip values at exactly 1 + if (xs[k].first == 1. || xs[k].second == 1.) continue; + + Coord ti = dom_i.valueAt(xs[k].first); + Coord tj = dom_j.valueAt(xs[k].second); + + CurveIntersection real(ti, tj, xs[k].point()); + result.push_back(real); + } + } + } + return result; +} + Point Curve::unitTangentAt(Coord t, unsigned n) const { std::vector<Point> derivs = pointAndDerivatives(t, n); @@ -108,6 +163,16 @@ Point Curve::unitTangentAt(Coord t, unsigned n) const return Point (0,0); }; +void Curve::feed(PathSink &sink, bool moveto_initial) const +{ + std::vector<Point> pts; + sbasis_to_bezier(pts, toSBasis(), 2); //TODO: use something better! + if (moveto_initial) { + sink.moveTo(initialPoint()); + } + sink.curveTo(pts[0], pts[1], pts[2]); +} + } // namespace Geom /* diff --git a/src/2geom/curve.h b/src/2geom/curve.h index 172fd7ddc..abbdb1100 100644 --- a/src/2geom/curve.h +++ b/src/2geom/curve.h @@ -35,27 +35,23 @@ */ -#ifndef _2GEOM_CURVE_H_ -#define _2GEOM_CURVE_H_ +#ifndef LIB2GEOM_SEEN_CURVE_H +#define LIB2GEOM_SEEN_CURVE_H #include <vector> +#include <boost/operators.hpp> #include <2geom/coord.h> #include <2geom/point.h> #include <2geom/interval.h> #include <2geom/sbasis.h> #include <2geom/d2.h> #include <2geom/affine.h> +#include <2geom/intersection.h> -namespace Geom -{ - -class Curve; - -struct CurveHelpers { -protected: - static int root_winding(Curve const &c, Point p); -}; +namespace Geom { +class PathSink; +typedef Intersection<> CurveIntersection; /** * @brief Abstract continuous curve on a plane defined on [0,1]. @@ -77,7 +73,9 @@ protected: * * @ingroup Curves */ -class Curve : private CurveHelpers { +class Curve + : boost::equality_comparable<Curve> +{ public: virtual ~Curve() {} @@ -86,26 +84,41 @@ public: /** @brief Retrieve the start of the curve. * @return The point corresponding to \f$\mathbf{C}(0)\f$. */ virtual Point initialPoint() const = 0; + /** Retrieve the end of the curve. * @return The point corresponding to \f$\mathbf{C}(1)\f$. */ virtual Point finalPoint() const = 0; + /** @brief Check whether the curve has exactly zero length. * @return True if the curve's initial point is exactly the same as its final point, and it contains - * no other points (its value set contains only one element). - */ + * no other points (its value set contains only one element). */ virtual bool isDegenerate() const = 0; + + /// Check whether the curve is a line segment. + virtual bool isLineSegment() const { return false; } + + /** @brief Get the interval of allowed time values. + * @return \f$[0, 1]\f$ */ + virtual Interval timeRange() const { + Interval tr(0, 1); + return tr; + } + /** @brief Evaluate the curve at a specified time value. * @param t Time value * @return \f$\mathbf{C}(t)\f$ */ virtual Point pointAt(Coord t) const { return pointAndDerivatives(t, 0).front(); } + /** @brief Evaluate one of the coordinates at the specified time value. * @param t Time value * @param d The dimension to evaluate * @return The specified coordinate of \f$\mathbf{C}(t)\f$ */ virtual Coord valueAt(Coord t, Dim2 d) const { return pointAt(t)[d]; } + /** @brief Evaluate the function at the specified time value. Allows curves to be used * as functors. */ virtual Point operator() (Coord t) const { return pointAt(t); } + /** @brief Evaluate the curve and its derivatives. * This will return a vector that contains the value of the curve and the specified number * of derivatives. However, the returned vector might contain less elements than specified @@ -125,6 +138,7 @@ public: * type. * @param p New starting point of the curve */ virtual void setInitial(Point const &v) = 0; + /** @brief Change the ending point of the curve. * After calling this method, it is guaranteed that \f$\mathbf{C}(0) = \mathbf{p}\f$, * and the curve is still continuous. The precise new shape of the curve varies @@ -140,12 +154,15 @@ public: * but it might not be the smallest such rectangle. This method is usually fast. * @return A rectangle that contains all points belonging to the curve. */ virtual Rect boundsFast() const = 0; + /** @brief Compute the curve's exact bounding box. * This method can be dramatically slower than boundsExact() depending on the curve type. * @return The smallest possible rectangle containing all of the curve's points. */ virtual Rect boundsExact() const = 0; + // I have no idea what the 'deg' parameter is for, so this is undocumented for now. virtual OptRect boundsLocal(OptInterval const &i, unsigned deg) const = 0; + /** @brief Compute the bounding box of a part of the curve. * Since this method returns the smallest possible bounding rectangle of the specified portion, * it can also be rather slow. @@ -163,21 +180,31 @@ public: * @return Pointer to a newly allocated curve, identical to the original */ virtual Curve *duplicate() const = 0; + /** @brief Transform this curve by an affine transformation. + * Because of this method, all curve types must be closed under affine + * transformations. + * @param m Affine describing the affine transformation */ + void transform(Affine const &m) { + *this *= m; + } + + virtual void operator*=(Translate const &tr) { *this *= Affine(tr); } + virtual void operator*=(Scale const &s) { *this *= Affine(s); } + virtual void operator*=(Rotate const &r) { *this *= Affine(r); } + virtual void operator*=(HShear const &hs) { *this *= Affine(hs); } + virtual void operator*=(VShear const &vs) { *this *= Affine(vs); } + virtual void operator*=(Zoom const &z) { *this *= Affine(z); } + virtual void operator*=(Affine const &m) = 0; + /** @brief Create a curve transformed by an affine transformation. - * This method returns a new curve instead modifying the existing one, because some curve - * types are not closed under affine transformations. The returned curve may be of different - * underlying type (as is the case for horizontal and vertical line segments). + * This method returns a new curve instead modifying the existing one. * @param m Affine describing the affine transformation * @return Pointer to a new, transformed curve */ - virtual Curve *transformed(Affine const &m) const = 0; - - /** @brief Translate the curve (i.e. displace by Point) - * This method modifies the curve; all curve types are closed under - * translations (the result can be expressed in its own curve type). - * This function yields the same result as transformed(m). - * @param p Point by which to translate the curve - * @return reference to self */ - virtual Curve &operator*=(Translate const &m) = 0; + virtual Curve *transformed(Affine const &m) const { + Curve *ret = duplicate(); + ret->transform(m); + return ret; + } /** @brief Create a curve that corresponds to a part of this curve. * For \f$a > b\f$, the returned portion will be reversed with respect to the original. @@ -190,13 +217,16 @@ public: * - \f$\mathbf{D}[ [0, 1] ] = \mathbf{C}[ [a?b] ]\f$, * where \f$[a?b] = [\min(a, b), \max(a, b)]\f$ */ virtual Curve *portion(Coord a, Coord b) const = 0; + /** @brief A version of that accepts an Interval. */ Curve *portion(Interval const &i) const { return portion(i.min(), i.max()); } + /** @brief Create a reversed version of this curve. * The result corresponds to <code>portion(1, 0)</code>, but this method might be faster. * @return Pointer to a new curve \f$\mathbf{D}\f$ such that * \f$\forall_{x \in [0, 1]} \mathbf{D}(x) = \mathbf{C}(1-x)\f$ */ virtual Curve *reverse() const { return portion(1, 0); } + /** @brief Create a derivative of this curve. * It's best to think of the derivative in physical terms: if the curve describes * the position of some object on the plane from time \f$t=0\f$ to \f$t=1\f$ as said in the @@ -215,22 +245,26 @@ public: * @param b Maximum time value to consider; \f$a < b\f$ * @return \f$q \in [a, b]: ||\mathbf{C}(q) - \mathbf{p}|| = \inf(\{r \in \mathbb{R} : ||\mathbf{C}(r) - \mathbf{p}||\})\f$ */ - virtual Coord nearestPoint( Point const& p, Coord a = 0, Coord b = 1 ) const; + virtual Coord nearestTime( Point const& p, Coord a = 0, Coord b = 1 ) const; + /** @brief A version that takes an Interval. */ - Coord nearestPoint(Point const &p, Interval const &i) const { - return nearestPoint(p, i.min(), i.max()); + Coord nearestTime(Point const &p, Interval const &i) const { + return nearestTime(p, i.min(), i.max()); } + /** @brief Compute time values at which the curve comes closest to a specified point. * @param p Query point * @param a Minimum time value to consider * @param b Maximum time value to consider; \f$a < b\f$ * @return Vector of points closest and equally far away from the query point */ - virtual std::vector<Coord> allNearestPoints( Point const& p, Coord from = 0, + virtual std::vector<Coord> allNearestTimes( Point const& p, Coord from = 0, Coord to = 1 ) const; + /** @brief A version that takes an Interval. */ - std::vector<Coord> allNearestPoints(Point const &p, Interval const &i) { - return allNearestPoints(p, i.min(), i.max()); + std::vector<Coord> allNearestTimes(Point const &p, Interval const &i) { + return allNearestTimes(p, i.min(), i.max()); } + /** @brief Compute the arc length of this curve. * For a curve \f$\mathbf{C}(t) = (C_x(t), C_y(t))\f$, arc length is defined for 2D curves as * \f[ \ell = \int_{0}^{1} \sqrt { [C_x'(t)]^2 + [C_y'(t)]^2 }\, \text{d}t \f] @@ -242,13 +276,28 @@ public: * @param tolerance Maximum allowed error * @return Total distance the curve's value travels on the plane when going from 0 to 1 */ virtual Coord length(Coord tolerance=0.01) const; + /** @brief Computes time values at which the curve intersects an axis-aligned line. * @param v The coordinate of the line * @param d Which axis the coordinate is on. X means a vertical line, Y a horizontal line. */ virtual std::vector<Coord> roots(Coord v, Dim2 d) const = 0; - /** @brief Compute the winding number of the curve at the specified point. - * @todo Does this makes sense for curves at all? */ - virtual int winding(Point const &p) const { return root_winding(*this, p); } + + /** @brief Compute the partial winding number of this curve. + * The partial winding number is equal to the difference between the number + * of roots at which the curve goes in the +Y direction and the number of roots + * at which the curve goes in the -Y direction. This method is mainly useful + * for implementing path winding calculation. It will ignore roots which + * are local maxima on the Y axis. + * @param p Point where the winding number should be determined + * @return Winding number contribution at p */ + virtual int winding(Point const &p) const; + + /// Compute intersections with another curve. + virtual std::vector<CurveIntersection> intersect(Curve const &other, Coord eps = EPSILON) const; + + /// Compute intersections of this curve with itself. + virtual std::vector<CurveIntersection> intersectSelf(Coord eps = EPSILON) const; + /** @brief Compute a vector tangent to the curve. * This will return an unit vector (a Point with length() equal to 1) that denotes a vector * tangent to the curve. This vector is defined as @@ -258,14 +307,9 @@ public: * derivative could be found. * @param t Time value * @param n The maximum order of derivative to consider - * @return Unit tangent vector \f$\mathbf{v}(t)\f$ - * @bug This method might currently break for the case of t being exactly 1. A workaround - * is to reverse the curve and use the negated unit tangent at 0 like this: - * @code - Curve *c_reverse = c.reverse(); - Point tangent = - c_reverse->unitTangentAt(0); - delete c_reverse; @endcode */ + * @return Unit tangent vector \f$\mathbf{v}(t)\f$ */ virtual Point unitTangentAt(Coord t, unsigned n = 3) const; + /** @brief Convert the curve to a symmetric power basis polynomial. * Symmetric power basis polynomials (S-basis for short) are numerical representations * of curves with excellent numerical properties. Most high level operations provided by 2Geom @@ -281,15 +325,22 @@ public: * of this curve. For example, for Bezier curves it returns the curve's order * multiplied by 2. */ virtual int degreesOfFreedom() const { return 0;} + /** @brief Test equality of two curves. + * Equality means that for any time value, the evaluation of either curve will yield + * the same value. This means non-degenerate curves are not equal to their reverses. + * Note that this tests for exact equality. * @return True if the curves are identical, false otherwise */ - virtual bool operator==(Curve const &c) const { return this == &c;} + virtual bool operator==(Curve const &c) const = 0; + + /** @brief Feed the curve to a PathSink */ + virtual void feed(PathSink &sink, bool moveto_initial) const; /// @} }; inline -Coord nearest_point(Point const& p, Curve const& c) { - return c.nearestPoint(p); +Coord nearest_time(Point const& p, Curve const& c) { + return c.nearestTime(p); } // for make benefit glorious library of Boost Pointer Container diff --git a/src/2geom/curves.h b/src/2geom/curves.h index 319b1924d..46fb6d973 100644 --- a/src/2geom/curves.h +++ b/src/2geom/curves.h @@ -1,10 +1,9 @@ -/** - * \file - * \brief Include all curve types +/** @file + * @brief Include all curve types *//* * Authors: - * MenTaLguY <mental@rydia.net> - * Marco Cecchetti <mrcekets at gmail.com> + * MenTaLguY <mental@rydia.net> + * Marco Cecchetti <mrcekets at gmail.com> * * Copyright 2007-2008 authors * @@ -38,9 +37,7 @@ #include <2geom/curve.h> #include <2geom/sbasis-curve.h> #include <2geom/bezier-curve.h> -#include <2geom/hvlinesegment.h> #include <2geom/elliptical-arc.h> -#include <2geom/svg-elliptical-arc.h> #endif // LIB2GEOM_SEEN_CURVES_H diff --git a/src/2geom/d2-sbasis.cpp b/src/2geom/d2-sbasis.cpp index 486ada9a2..ebec16fdd 100644 --- a/src/2geom/d2-sbasis.cpp +++ b/src/2geom/d2-sbasis.cpp @@ -200,7 +200,7 @@ Point unitTangentAt(D2<SBasis> const & a, Coord t, unsigned n) static void set_first_point(Piecewise<D2<SBasis> > &f, Point a){ if ( f.empty() ){ - f.concat(Piecewise<D2<SBasis> >(D2<SBasis>(Linear(a[X]),Linear(a[Y])))); + f.concat(Piecewise<D2<SBasis> >(D2<SBasis>(SBasis(Linear(a[X])), SBasis(Linear(a[Y]))))); return; } for (unsigned dim=0; dim<2; dim++){ @@ -213,7 +213,7 @@ static void set_first_point(Piecewise<D2<SBasis> > &f, Point a){ } static void set_last_point(Piecewise<D2<SBasis> > &f, Point a){ if ( f.empty() ){ - f.concat(Piecewise<D2<SBasis> >(D2<SBasis>(Linear(a[X]),Linear(a[Y])))); + f.concat(Piecewise<D2<SBasis> >(D2<SBasis>(SBasis(Linear(a[X])), SBasis(Linear(a[Y]))))); return; } for (unsigned dim=0; dim<2; dim++){ diff --git a/src/2geom/d2-sbasis.h b/src/2geom/d2-sbasis.h index 965ec8746..a0769b314 100644 --- a/src/2geom/d2-sbasis.h +++ b/src/2geom/d2-sbasis.h @@ -35,11 +35,11 @@ * */ -#ifdef SEEN_LIB2GEOM_D2_H /*This is intentional: we don't actually want anyone to +#ifdef LIB2GEOM_SEEN_D2_H /*This is intentional: we don't actually want anyone to include this, other than D2.h. If somone else tries, D2 won't be defined. If it is, this will already be included. */ -#ifndef SEEN_LIB2GEOM_D2_SBASIS_H -#define SEEN_LIB2GEOM_D2_SBASIS_H +#ifndef LIB2GEOM_SEEN_D2_SBASIS_H +#define LIB2GEOM_SEEN_D2_SBASIS_H #include <2geom/sbasis.h> #include <2geom/sbasis-2d.h> @@ -149,8 +149,8 @@ std::vector<std::vector<Interval> > level_sets( D2<SBasis> const &f, std::vector } -#endif -#endif +#endif // LIB2GEOM_SEEN_D2_SBASIS_H +#endif // LIB2GEOM_SEEN_D2_H /* diff --git a/src/2geom/d2.h b/src/2geom/d2.h index 5fc955854..dca6fa614 100644 --- a/src/2geom/d2.h +++ b/src/2geom/d2.h @@ -29,32 +29,34 @@ * */ -#ifndef SEEN_LIB2GEOM_D2_H -#define SEEN_LIB2GEOM_D2_H +#ifndef LIB2GEOM_SEEN_D2_H +#define LIB2GEOM_SEEN_D2_H +#include <iterator> +#include <boost/concept/assert.hpp> +#include <boost/iterator/transform_iterator.hpp> #include <2geom/point.h> #include <2geom/interval.h> #include <2geom/affine.h> #include <2geom/rect.h> - -#include <boost/concept_check.hpp> #include <2geom/concepts.h> -namespace Geom{ +namespace Geom { /** - * The D2 class takes two instances of a scalar data type and treats them - * like a point. All operations which make sense on a point are defined for D2. - * A D2<double> is a Point. A D2<Interval> is a standard axis aligned rectangle. - * D2<SBasis> provides a 2d parametric function which maps t to a point - * x(t), y(t) + * @brief Adaptor that creates 2D functions from 1D ones. + * @ingroup Fragments */ -template <class T> -class D2{ - //BOOST_CLASS_REQUIRE(T, boost, AssignableConcept); - private: +template <typename T> +class D2 +{ +private: T f[2]; - public: +public: + typedef T D1Value; + typedef T &D1Reference; + typedef T const &D1ConstReference; + D2() {f[X] = f[Y] = T();} explicit D2(Point const &a) { f[X] = T(a[X]); f[Y] = T(a[Y]); @@ -65,38 +67,72 @@ class D2{ f[Y] = b; } + template <typename Iter> + D2(Iter first, Iter last) { + typedef typename std::iterator_traits<Iter>::value_type V; + typedef typename boost::transform_iterator<GetAxis<X,V>, Iter> XIter; + typedef typename boost::transform_iterator<GetAxis<Y,V>, Iter> YIter; + + XIter xfirst(first, GetAxis<X,V>()), xlast(last, GetAxis<X,V>()); + f[X] = T(xfirst, xlast); + YIter yfirst(first, GetAxis<Y,V>()), ylast(last, GetAxis<Y,V>()); + f[Y] = T(yfirst, ylast); + } + + D2(std::vector<Point> const &vec) { + typedef Point V; + typedef std::vector<Point>::const_iterator Iter; + typedef boost::transform_iterator<GetAxis<X,V>, Iter> XIter; + typedef boost::transform_iterator<GetAxis<Y,V>, Iter> YIter; + + XIter xfirst(vec.begin(), GetAxis<X,V>()), xlast(vec.end(), GetAxis<X,V>()); + f[X] = T(xfirst, xlast); + YIter yfirst(vec.begin(), GetAxis<Y,V>()), ylast(vec.end(), GetAxis<Y,V>()); + f[Y] = T(yfirst, ylast); + } + //TODO: ask mental about operator= as seen in Point T& operator[](unsigned i) { return f[i]; } T const & operator[](unsigned i) const { return f[i]; } + Point point(unsigned i) const { + Point ret(f[X][i], f[Y][i]); + return ret; + } //IMPL: FragmentConcept typedef Point output_type; bool isZero(double eps=EPSILON) const { - boost::function_requires<FragmentConcept<T> >(); + BOOST_CONCEPT_ASSERT((FragmentConcept<T>)); return f[X].isZero(eps) && f[Y].isZero(eps); } bool isConstant(double eps=EPSILON) const { - boost::function_requires<FragmentConcept<T> >(); + BOOST_CONCEPT_ASSERT((FragmentConcept<T>)); return f[X].isConstant(eps) && f[Y].isConstant(eps); } bool isFinite() const { - boost::function_requires<FragmentConcept<T> >(); + BOOST_CONCEPT_ASSERT((FragmentConcept<T>)); return f[X].isFinite() && f[Y].isFinite(); } Point at0() const { - boost::function_requires<FragmentConcept<T> >(); + BOOST_CONCEPT_ASSERT((FragmentConcept<T>)); return Point(f[X].at0(), f[Y].at0()); } Point at1() const { - boost::function_requires<FragmentConcept<T> >(); + BOOST_CONCEPT_ASSERT((FragmentConcept<T>)); return Point(f[X].at1(), f[Y].at1()); } + Point pointAt(double t) const { + BOOST_CONCEPT_ASSERT((FragmentConcept<T>)); + return (*this)(t); + } Point valueAt(double t) const { - boost::function_requires<FragmentConcept<T> >(); + // TODO: remove this alias + BOOST_CONCEPT_ASSERT((FragmentConcept<T>)); return (*this)(t); } std::vector<Point > valueAndDerivatives(double t, unsigned n) const { + BOOST_CONCEPT_ASSERT((FragmentConcept<T>)); std::vector<Coord> x = f[X].valueAndDerivatives(t, n), y = f[Y].valueAndDerivatives(t, n); // always returns a vector of size n+1 std::vector<Point> res(n+1); @@ -106,7 +142,7 @@ class D2{ return res; } D2<SBasis> toSBasis() const { - boost::function_requires<FragmentConcept<T> >(); + BOOST_CONCEPT_ASSERT((FragmentConcept<T>)); return D2<SBasis>(f[X].toSBasis(), f[Y].toSBasis()); } @@ -115,33 +151,33 @@ class D2{ }; template <typename T> inline D2<T> reverse(const D2<T> &a) { - boost::function_requires<FragmentConcept<T> >(); + BOOST_CONCEPT_ASSERT((FragmentConcept<T>)); return D2<T>(reverse(a[X]), reverse(a[Y])); } template <typename T> inline D2<T> portion(const D2<T> &a, Coord f, Coord t) { - boost::function_requires<FragmentConcept<T> >(); + BOOST_CONCEPT_ASSERT((FragmentConcept<T>)); return D2<T>(portion(a[X], f, t), portion(a[Y], f, t)); } template <typename T> inline D2<T> portion(const D2<T> &a, Interval i) { - boost::function_requires<FragmentConcept<T> >(); + BOOST_CONCEPT_ASSERT((FragmentConcept<T>)); return D2<T>(portion(a[X], i), portion(a[Y], i)); } -//IMPL: boost::EqualityComparableConcept +//IMPL: EqualityComparableConcept template <typename T> inline bool operator==(D2<T> const &a, D2<T> const &b) { - boost::function_requires<boost::EqualityComparableConcept<T> >(); + BOOST_CONCEPT_ASSERT((EqualityComparableConcept<T>)); return a[0]==b[0] && a[1]==b[1]; } template <typename T> inline bool operator!=(D2<T> const &a, D2<T> const &b) { - boost::function_requires<boost::EqualityComparableConcept<T> >(); + BOOST_CONCEPT_ASSERT((EqualityComparableConcept<T>)); return a[0]!=b[0] || a[1]!=b[1]; } @@ -149,7 +185,7 @@ operator!=(D2<T> const &a, D2<T> const &b) { template <typename T> inline bool are_near(D2<T> const &a, D2<T> const &b, double tol) { - boost::function_requires<NearConcept<T> >(); + BOOST_CONCEPT_ASSERT((NearConcept<T>)); return are_near(a[0], b[0], tol) && are_near(a[1], b[1], tol); } @@ -157,7 +193,7 @@ are_near(D2<T> const &a, D2<T> const &b, double tol) { template <typename T> inline D2<T> operator+(D2<T> const &a, D2<T> const &b) { - boost::function_requires<AddableConcept<T> >(); + BOOST_CONCEPT_ASSERT((AddableConcept<T>)); D2<T> r; for(unsigned i = 0; i < 2; i++) @@ -167,7 +203,7 @@ operator+(D2<T> const &a, D2<T> const &b) { template <typename T> inline D2<T> operator-(D2<T> const &a, D2<T> const &b) { - boost::function_requires<AddableConcept<T> >(); + BOOST_CONCEPT_ASSERT((AddableConcept<T>)); D2<T> r; for(unsigned i = 0; i < 2; i++) @@ -177,7 +213,7 @@ operator-(D2<T> const &a, D2<T> const &b) { template <typename T> inline D2<T> operator+=(D2<T> &a, D2<T> const &b) { - boost::function_requires<AddableConcept<T> >(); + BOOST_CONCEPT_ASSERT((AddableConcept<T>)); for(unsigned i = 0; i < 2; i++) a[i] += b[i]; @@ -186,7 +222,7 @@ operator+=(D2<T> &a, D2<T> const &b) { template <typename T> inline D2<T> operator-=(D2<T> &a, D2<T> const & b) { - boost::function_requires<AddableConcept<T> >(); + BOOST_CONCEPT_ASSERT((AddableConcept<T>)); for(unsigned i = 0; i < 2; i++) a[i] -= b[i]; @@ -197,7 +233,7 @@ operator-=(D2<T> &a, D2<T> const & b) { template <typename T> inline D2<T> operator-(D2<T> const & a) { - boost::function_requires<ScalableConcept<T> >(); + BOOST_CONCEPT_ASSERT((ScalableConcept<T>)); D2<T> r; for(unsigned i = 0; i < 2; i++) r[i] = -a[i]; @@ -206,7 +242,7 @@ operator-(D2<T> const & a) { template <typename T> inline D2<T> operator*(D2<T> const & a, Point const & b) { - boost::function_requires<ScalableConcept<T> >(); + BOOST_CONCEPT_ASSERT((ScalableConcept<T>)); D2<T> r; for(unsigned i = 0; i < 2; i++) @@ -216,7 +252,7 @@ operator*(D2<T> const & a, Point const & b) { template <typename T> inline D2<T> operator/(D2<T> const & a, Point const & b) { - boost::function_requires<ScalableConcept<T> >(); + BOOST_CONCEPT_ASSERT((ScalableConcept<T>)); //TODO: b==0? D2<T> r; for(unsigned i = 0; i < 2; i++) @@ -226,7 +262,7 @@ operator/(D2<T> const & a, Point const & b) { template <typename T> inline D2<T> operator*=(D2<T> &a, Point const & b) { - boost::function_requires<ScalableConcept<T> >(); + BOOST_CONCEPT_ASSERT((ScalableConcept<T>)); for(unsigned i = 0; i < 2; i++) a[i] *= b[i]; @@ -235,7 +271,7 @@ operator*=(D2<T> &a, Point const & b) { template <typename T> inline D2<T> operator/=(D2<T> &a, Point const & b) { - boost::function_requires<ScalableConcept<T> >(); + BOOST_CONCEPT_ASSERT((ScalableConcept<T>)); //TODO: b==0? for(unsigned i = 0; i < 2; i++) a[i] /= b[i]; @@ -253,8 +289,8 @@ inline D2<T> operator/=(D2<T> & a, double b) { a[0] /= b; a[1] /= b; return a; } template<typename T> D2<T> operator*(D2<T> const &v, Affine const &m) { - boost::function_requires<AddableConcept<T> >(); - boost::function_requires<ScalableConcept<T> >(); + BOOST_CONCEPT_ASSERT((AddableConcept<T>)); + BOOST_CONCEPT_ASSERT((ScalableConcept<T>)); D2<T> ret; for(unsigned i = 0; i < 2; i++) ret[i] = v[X] * m[i] + v[Y] * m[i + 2] + m[i + 4]; @@ -265,7 +301,7 @@ D2<T> operator*(D2<T> const &v, Affine const &m) { template <typename T> inline D2<T> operator*(D2<T> const & a, T const & b) { - boost::function_requires<MultiplicableConcept<T> >(); + BOOST_CONCEPT_ASSERT((MultiplicableConcept<T>)); D2<T> ret; for(unsigned i = 0; i < 2; i++) ret[i] = a[i] * b; @@ -278,7 +314,7 @@ operator*(D2<T> const & a, T const & b) { template <typename T> inline D2<T> operator+(D2<T> const & a, Point b) { - boost::function_requires<OffsetableConcept<T> >(); + BOOST_CONCEPT_ASSERT((OffsetableConcept<T>)); D2<T> r; for(unsigned i = 0; i < 2; i++) r[i] = a[i] + b[i]; @@ -287,7 +323,7 @@ operator+(D2<T> const & a, Point b) { template <typename T> inline D2<T> operator-(D2<T> const & a, Point b) { - boost::function_requires<OffsetableConcept<T> >(); + BOOST_CONCEPT_ASSERT((OffsetableConcept<T>)); D2<T> r; for(unsigned i = 0; i < 2; i++) r[i] = a[i] - b[i]; @@ -296,7 +332,7 @@ operator-(D2<T> const & a, Point b) { template <typename T> inline D2<T> operator+=(D2<T> & a, Point b) { - boost::function_requires<OffsetableConcept<T> >(); + BOOST_CONCEPT_ASSERT((OffsetableConcept<T>)); for(unsigned i = 0; i < 2; i++) a[i] += b[i]; return a; @@ -304,7 +340,7 @@ operator+=(D2<T> & a, Point b) { template <typename T> inline D2<T> operator-=(D2<T> & a, Point b) { - boost::function_requires<OffsetableConcept<T> >(); + BOOST_CONCEPT_ASSERT((OffsetableConcept<T>)); for(unsigned i = 0; i < 2; i++) a[i] -= b[i]; return a; @@ -313,8 +349,8 @@ operator-=(D2<T> & a, Point b) { template <typename T> inline T dot(D2<T> const & a, D2<T> const & b) { - boost::function_requires<AddableConcept<T> >(); - boost::function_requires<MultiplicableConcept<T> >(); + BOOST_CONCEPT_ASSERT((AddableConcept<T>)); + BOOST_CONCEPT_ASSERT((MultiplicableConcept<T>)); T r; for(unsigned i = 0; i < 2; i++) @@ -328,8 +364,8 @@ dot(D2<T> const & a, D2<T> const & b) { template <typename T> inline T dot(D2<T> const & a, Point const & b) { - boost::function_requires<AddableConcept<T> >(); - boost::function_requires<ScalableConcept<T> >(); + BOOST_CONCEPT_ASSERT((AddableConcept<T>)); + BOOST_CONCEPT_ASSERT((ScalableConcept<T>)); T r; for(unsigned i = 0; i < 2; i++) { @@ -344,8 +380,8 @@ dot(D2<T> const & a, Point const & b) { template <typename T> inline T cross(D2<T> const & a, D2<T> const & b) { - boost::function_requires<ScalableConcept<T> >(); - boost::function_requires<MultiplicableConcept<T> >(); + BOOST_CONCEPT_ASSERT((ScalableConcept<T>)); + BOOST_CONCEPT_ASSERT((MultiplicableConcept<T>)); return a[1] * b[0] - a[0] * b[1]; } @@ -355,7 +391,7 @@ cross(D2<T> const & a, D2<T> const & b) { template <typename T> inline D2<T> rot90(D2<T> const & a) { - boost::function_requires<ScalableConcept<T> >(); + BOOST_CONCEPT_ASSERT((ScalableConcept<T>)); return D2<T>(-a[Y], a[X]); } @@ -429,26 +465,28 @@ inline std::ostream &operator<< (std::ostream &out_file, const Geom::D2<T> &in_d #include <2geom/d2-sbasis.h> -namespace Geom{ +namespace Geom { //Some D2 Fragment implementation which requires rect: template <typename T> OptRect bounds_fast(const D2<T> &a) { - boost::function_requires<FragmentConcept<T> >(); + BOOST_CONCEPT_ASSERT((FragmentConcept<T>)); return OptRect(bounds_fast(a[X]), bounds_fast(a[Y])); } template <typename T> OptRect bounds_exact(const D2<T> &a) { - boost::function_requires<FragmentConcept<T> >(); + BOOST_CONCEPT_ASSERT((FragmentConcept<T>)); return OptRect(bounds_exact(a[X]), bounds_exact(a[Y])); } template <typename T> OptRect bounds_local(const D2<T> &a, const OptInterval &t) { - boost::function_requires<FragmentConcept<T> >(); + BOOST_CONCEPT_ASSERT((FragmentConcept<T>)); return OptRect(bounds_local(a[X], t), bounds_local(a[Y], t)); } -}; +} // end namespace Geom + +#endif /* Local Variables: mode:c++ @@ -459,4 +497,3 @@ OptRect bounds_local(const D2<T> &a, const OptInterval &t) { End: */ // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : -#endif diff --git a/src/2geom/ellipse.cpp b/src/2geom/ellipse.cpp index 2686844b2..4b5ad9762 100644 --- a/src/2geom/ellipse.cpp +++ b/src/2geom/ellipse.cpp @@ -1,10 +1,11 @@ -/* - * Ellipse Curve - * +/** @file + * @brief Ellipse shape + *//* * Authors: - * Marco Cecchetti <mrcekets at gmail.com> + * Marco Cecchetti <mrcekets at gmail.com> + * Krzysztof Kosiński <tweenk.pl@gmail.com> * - * Copyright 2008 authors + * Copyright 2008-2014 Authors * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public @@ -30,138 +31,161 @@ * the specific language governing rights and limitations. */ - #include <2geom/ellipse.h> -#include <2geom/svg-elliptical-arc.h> +#include <2geom/elliptical-arc.h> #include <2geom/numeric/fitting-tool.h> #include <2geom/numeric/fitting-model.h> -using std::swap; +namespace Geom { -namespace Geom -{ +Ellipse::Ellipse(Geom::Circle const &c) + : _center(c.center()) + , _rays(c.radius(), c.radius()) + , _angle(0) +{} -void Ellipse::set(double A, double B, double C, double D, double E, double F) +void Ellipse::setCoefficients(double A, double B, double C, double D, double E, double F) { double den = 4*A*C - B*B; - if ( den == 0 ) - { - THROW_LOGICALERROR("den == 0, while computing ellipse centre"); + if (den == 0) { + THROW_RANGEERROR("den == 0, while computing ellipse centre"); } - m_centre[X] = (B*E - 2*C*D) / den; - m_centre[Y] = (B*D - 2*A*E) / den; + _center[X] = (B*E - 2*C*D) / den; + _center[Y] = (B*D - 2*A*E) / den; // evaluate the a coefficient of the ellipse equation in normal form // E(x,y) = a*(x-cx)^2 + b*(x-cx)*(y-cy) + c*(y-cy)^2 = 1 // where b = a*B , c = a*C, (cx,cy) == centre - double num = A * sqr(m_centre[X]) - + B * m_centre[X] * m_centre[Y] - + C * sqr(m_centre[Y]) + double num = A * sqr(_center[X]) + + B * _center[X] * _center[Y] + + C * sqr(_center[Y]) - F; //evaluate ellipse rotation angle - double rot = std::atan2( -B, -(A - C) )/2; -// std::cerr << "rot = " << rot << std::endl; - bool swap_axes = false; - if ( are_near(rot, 0) ) rot = 0; - if ( are_near(rot, M_PI/2) || rot < 0 ) - { - swap_axes = true; - } + _angle = std::atan2( -B, -(A - C) )/2; // evaluate the length of the ellipse rays - double cosrot = std::cos(rot); - double sinrot = std::sin(rot); + double sinrot, cosrot; + sincos(_angle, sinrot, cosrot); double cos2 = cosrot * cosrot; double sin2 = sinrot * sinrot; double cossin = cosrot * sinrot; den = A * cos2 + B * cossin + C * sin2; - if ( den == 0 ) - { - THROW_LOGICALERROR("den == 0, while computing 'rx' coefficient"); + if (den == 0) { + THROW_RANGEERROR("den == 0, while computing 'rx' coefficient"); } - double rx2 = num/den; - if ( rx2 < 0 ) - { - THROW_LOGICALERROR("rx2 < 0, while computing 'rx' coefficient"); + double rx2 = num / den; + if (rx2 < 0) { + THROW_RANGEERROR("rx2 < 0, while computing 'rx' coefficient"); } - double rx = std::sqrt(rx2); + _rays[X] = std::sqrt(rx2); den = C * cos2 - B * cossin + A * sin2; - if ( den == 0 ) - { - THROW_LOGICALERROR("den == 0, while computing 'ry' coefficient"); + if (den == 0) { + THROW_RANGEERROR("den == 0, while computing 'ry' coefficient"); } - double ry2 = num/den; - if ( ry2 < 0 ) - { - THROW_LOGICALERROR("ry2 < 0, while computing 'rx' coefficient"); + double ry2 = num / den; + if (ry2 < 0) { + THROW_RANGEERROR("ry2 < 0, while computing 'rx' coefficient"); } - double ry = std::sqrt(ry2); + _rays[Y] = std::sqrt(ry2); // the solution is not unique so we choose always the ellipse // with a rotation angle between 0 and PI/2 - if ( swap_axes ) swap(rx, ry); - if ( are_near(rot, M_PI/2) - || are_near(rot, -M_PI/2) - || are_near(rx, ry) ) - { - rot = 0; - } - else if ( rot < 0 ) - { - rot += M_PI/2; + makeCanonical(); +} + +Point Ellipse::initialPoint() const +{ + Coord sinrot, cosrot; + sincos(_angle, sinrot, cosrot); + Point p(ray(X) * cosrot + center(X), ray(X) * sinrot + center(Y)); + return p; +} + + +Affine Ellipse::unitCircleTransform() const +{ + Affine ret = Scale(ray(X), ray(Y)) * Rotate(_angle); + ret.setTranslation(center()); + return ret; +} + +Affine Ellipse::inverseUnitCircleTransform() const +{ + if (ray(X) == 0 || ray(Y) == 0) { + THROW_RANGEERROR("a degenerate ellipse doesn't have an inverse unit circle transform"); } + Affine ret = Translate(-center()) * Rotate(-_angle) * Scale(1/ray(X), 1/ray(Y)); + return ret; +} + + +LineSegment Ellipse::axis(Dim2 d) const +{ + Point a(0, 0), b(0, 0); + a[d] = -1; + b[d] = 1; + LineSegment ls(a, b); + ls.transform(unitCircleTransform()); + return ls; +} - m_ray[X] = rx; - m_ray[Y] = ry; - m_angle = rot; +LineSegment Ellipse::semiaxis(Dim2 d, int sign) const +{ + Point a(0, 0), b(0, 0); + b[d] = sgn(sign); + LineSegment ls(a, b); + ls.transform(unitCircleTransform()); + return ls; } -std::vector<double> Ellipse::implicit_form_coefficients() const +std::vector<double> Ellipse::coefficients() const { - if (ray(X) == 0 || ray(Y) == 0) - { - THROW_LOGICALERROR("a degenerate ellipse doesn't own an implicit form"); + std::vector<double> c(6); + coefficients(c[0], c[1], c[2], c[3], c[4], c[5]); + return c; +} + +void Ellipse::coefficients(Coord &A, Coord &B, Coord &C, Coord &D, Coord &E, Coord &F) const +{ + if (ray(X) == 0 || ray(Y) == 0) { + THROW_RANGEERROR("a degenerate ellipse doesn't have an implicit form"); } - std::vector<double> coeff(6); - double cosrot = std::cos(rot_angle()); - double sinrot = std::sin(rot_angle()); + double cosrot, sinrot; + sincos(_angle, sinrot, cosrot); double cos2 = cosrot * cosrot; double sin2 = sinrot * sinrot; double cossin = cosrot * sinrot; double invrx2 = 1 / (ray(X) * ray(X)); double invry2 = 1 / (ray(Y) * ray(Y)); - coeff[0] = invrx2 * cos2 + invry2 * sin2; - coeff[1] = 2 * (invrx2 - invry2) * cossin; - coeff[2] = invrx2 * sin2 + invry2 * cos2; - coeff[3] = -(2 * coeff[0] * center(X) + coeff[1] * center(Y)); - coeff[4] = -(2 * coeff[2] * center(Y) + coeff[1] * center(X)); - coeff[5] = coeff[0] * center(X) * center(X) - + coeff[1] * center(X) * center(Y) - + coeff[2] * center(Y) * center(Y) - - 1; - return coeff; + A = invrx2 * cos2 + invry2 * sin2; + B = 2 * (invrx2 - invry2) * cossin; + C = invrx2 * sin2 + invry2 * cos2; + D = -2 * A * center(X) - B * center(Y); + E = -2 * C * center(Y) - B * center(X); + F = A * center(X) * center(X) + + B * center(X) * center(Y) + + C * center(Y) * center(Y) + - 1; } -void Ellipse::set(std::vector<Point> const& points) +void Ellipse::fit(std::vector<Point> const &points) { size_t sz = points.size(); - if (sz < 5) - { + if (sz < 5) { THROW_RANGEERROR("fitting error: too few points passed"); } NL::LFMEllipse model; NL::least_squeares_fitter<NL::LFMEllipse> fitter(model, sz); - for (size_t i = 0; i < sz; ++i) - { + for (size_t i = 0; i < sz; ++i) { fitter.append(points[i]); } fitter.update(); @@ -172,109 +196,443 @@ void Ellipse::set(std::vector<Point> const& points) EllipticalArc * -Ellipse::arc(Point const& initial, Point const& inner, Point const& final, - bool _svg_compliant) +Ellipse::arc(Point const &ip, Point const &inner, Point const &fp) { - Point sp_cp = initial - center(); - Point ep_cp = final - center(); - Point ip_cp = inner - center(); + // This is resistant to degenerate ellipses: + // both flags evaluate to false in that case. + + bool large_arc_flag = false; + bool sweep_flag = false; + + // Determination of large arc flag: + // large_arc is false when the inner point is on the same side + // of the center---initial point line as the final point, AND + // is on the same side of the center---final point line as the + // initial point. + // Additionally, large_arc is always false when we have exactly + // 1/2 of an arc, i.e. the cross product of the center -> initial point + // and center -> final point vectors is zero. + // Negating the above leads to the condition for large_arc being true. + Point fv = fp - _center; + Point iv = ip - _center; + Point innerv = inner - _center; + double ifcp = cross(fv, iv); + + if (ifcp != 0 && (sgn(cross(fv, innerv)) != sgn(ifcp) || + sgn(cross(iv, innerv)) != sgn(-ifcp))) + { + large_arc_flag = true; + } - double angle1 = angle_between(sp_cp, ep_cp); - double angle2 = angle_between(sp_cp, ip_cp); - double angle3 = angle_between(ip_cp, ep_cp); + //cross(-iv, fv) && large_arc_flag + + + // Determination of sweep flag: + // For clarity, let's assume that Y grows up. Then the cross product + // is positive for points on the left side of a vector and negative + // on the right side of a vector. + // + // cross(?, v) > 0 + // o-------------------> + // cross(?, v) < 0 + // + // If the arc is small (large_arc_flag is false) and the final point + // is on the right side of the vector initial point -> center, + // we have to go in the direction of increasing angles + // (counter-clockwise) and the sweep flag is true. + // If the arc is large, the opposite is true, since we have to reach + // the final point going the long way - in the other direction. + // We can express this observation as: + // cross(_center - ip, fp - _center) < 0 xor large_arc flag + // This is equal to: + // cross(-iv, fv) < 0 xor large_arc flag + // But cross(-iv, fv) is equal to cross(fv, iv) due to antisymmetry + // of the cross product, so we end up with the condition below. + if ((ifcp < 0) ^ large_arc_flag) { + sweep_flag = true; + } - bool large_arc_flag = true; - bool sweep_flag = true; + EllipticalArc *ret_arc = new EllipticalArc(ip, ray(X), ray(Y), rotationAngle(), + large_arc_flag, sweep_flag, fp); + return ret_arc; +} - if ( angle1 > 0 ) - { - if ( angle2 > 0 && angle3 > 0 ) - { - large_arc_flag = false; - sweep_flag = true; - } - else - { - large_arc_flag = true; - sweep_flag = false; +Ellipse &Ellipse::operator*=(Rotate const &r) +{ + _angle += r.angle(); + _center *= r; + return *this; +} + +Ellipse &Ellipse::operator*=(Affine const& m) +{ + Affine a = Scale(ray(X), ray(Y)) * Rotate(_angle); + Affine mwot = m.withoutTranslation(); + Affine am = a * mwot; + Point new_center = _center * m; + + if (are_near(am.descrim(), 0)) { + double angle; + if (am[0] != 0) { + angle = std::atan2(am[2], am[0]); + } else if (am[1] != 0) { + angle = std::atan2(am[3], am[1]); + } else { + angle = M_PI/2; } + Point v = Point::polar(angle) * am; + _center = new_center; + _rays[X] = L2(v); + _rays[Y] = 0; + _angle = atan2(v); + return *this; } - else - { - if ( angle2 < 0 && angle3 < 0 ) - { - large_arc_flag = false; - sweep_flag = false; - } - else - { - large_arc_flag = true; - sweep_flag = true; - } + + std::vector<double> coeff = coefficients(); + Affine q( coeff[0], coeff[1]/2, + coeff[1]/2, coeff[2], + 0, 0 ); + + Affine invm = mwot.inverse(); + q = invm * q ; + std::swap(invm[1], invm[2]); + q *= invm; + setCoefficients(q[0], 2*q[1], q[3], 0, 0, -1); + _center = new_center; + + return *this; +} + +Ellipse Ellipse::canonicalForm() const +{ + Ellipse result(*this); + result.makeCanonical(); + return result; +} + +void Ellipse::makeCanonical() +{ + if (_rays[X] == _rays[Y]) { + _angle = 0; + return; + } + + if (_angle < 0) { + _angle += M_PI; + } + if (_angle >= M_PI/2) { + std::swap(_rays[X], _rays[Y]); + _angle -= M_PI/2; } +} - EllipticalArc *ret_arc; - if (_svg_compliant) { - ret_arc = new SVGEllipticalArc(initial, ray(X), ray(Y), rot_angle(), - large_arc_flag, sweep_flag, final); +Point Ellipse::pointAt(Coord t) const +{ + Point p = Point::polar(t); + p *= unitCircleTransform(); + return p; +} + +Coord Ellipse::valueAt(Coord t, Dim2 d) const +{ + Coord sinrot, cosrot, cost, sint; + sincos(rotationAngle(), sinrot, cosrot); + sincos(t, sint, cost); + + if ( d == X ) { + return ray(X) * cosrot * cost + - ray(Y) * sinrot * sint + + center(X); } else { - ret_arc = new EllipticalArc(initial, ray(X), ray(Y), rot_angle(), - large_arc_flag, sweep_flag, final); + return ray(X) * sinrot * cost + + ray(Y) * cosrot * sint + + center(Y); } - return ret_arc; } -Ellipse Ellipse::transformed(Affine const& m) const +Coord Ellipse::timeAt(Point const &p) const { - double cosrot = std::cos(rot_angle()); - double sinrot = std::sin(rot_angle()); - Affine A( ray(X) * cosrot, ray(X) * sinrot, - -ray(Y) * sinrot, ray(Y) * cosrot, - 0, 0 ); - Point new_center = center() * m; - Affine M = m.withoutTranslation(); - Affine AM = A * M; - if ( are_near(std::sqrt(fabs(AM.det())), 0) ) - { - double angle; - if (AM[0] != 0) - { - angle = std::atan2(AM[2], AM[0]); + // degenerate ellipse is basically a reparametrized line segment + if (ray(X) == 0 || ray(Y) == 0) { + if (ray(X) != 0) { + return asin(Line(axis(X)).timeAt(p)); + } else if (ray(Y) != 0) { + return acos(Line(axis(Y)).timeAt(p)); + } else { + return 0; } - else if (AM[1] != 0) - { - angle = std::atan2(AM[3], AM[1]); + } + Affine iuct = inverseUnitCircleTransform(); + return Angle(atan2(p * iuct)).radians0(); // return a value in [0, 2pi) +} + +Point Ellipse::unitTangentAt(Coord t) const +{ + Point p = Point::polar(t + M_PI/2); + p *= unitCircleTransform().withoutTranslation(); + p.normalize(); + return p; +} + +bool Ellipse::contains(Point const &p) const +{ + Point tp = p * inverseUnitCircleTransform(); + return tp.length() <= 1; +} + +std::vector<ShapeIntersection> Ellipse::intersect(Line const &line) const +{ + + std::vector<ShapeIntersection> result; + + if (line.isDegenerate()) return result; + if (ray(X) == 0 || ray(Y) == 0) { + // TODO intersect with line segment. + return result; + } + + // Ax^2 + Bxy + Cy^2 + Dx + Ey + F + Coord A, B, C, D, E, F; + coefficients(A, B, C, D, E, F); + Affine iuct = inverseUnitCircleTransform(); + + if (line.isHorizontal()) { + // substitute y into the ellipse equation and solve + Coord y = line.initialPoint()[Y]; + std::vector<Coord> xs = solve_quadratic(A, B*y + D, C*y*y + E*y + F); + for (unsigned i = 0; i < xs.size(); ++i) { + Point p(xs[i], y); + result.push_back(ShapeIntersection(atan2(p * iuct), line.timeAt(p), p)); } - else - { - angle = M_PI/2; + return result; + } + if (line.isVertical()) { + // substitute y into the ellipse equation and solve + Coord x = line.initialPoint()[X]; + std::vector<Coord> ys = solve_quadratic(C, B*x + E, A*x*x + D*x + F); + for (unsigned i = 0; i < ys.size(); ++i) { + Point p(x, ys[i]); + result.push_back(ShapeIntersection(atan2(p * iuct), line.timeAt(p), p)); } - Point V(std::cos(angle), std::sin(angle)); - V *= AM; - double rx = L2(V); - angle = atan2(V); - return Ellipse(new_center[X], new_center[Y], rx, 0, angle); + return result; } - std::vector<double> coeff = implicit_form_coefficients(); - Affine Q( coeff[0], coeff[1]/2, - coeff[1]/2, coeff[2], - 0, 0 ); + // generic case + Coord a, b, c; + line.coefficients(a, b, c); + + // y = -a/b x - C/B + // TODO: when is it better to substitute X? + Coord q = -a/b; + Coord r = -c/b; - Affine invm = M.inverse(); - Q = invm * Q ; - swap( invm[1], invm[2] ); - Q *= invm; - Ellipse e(Q[0], 2*Q[1], Q[3], 0, 0, -1); - e.m_centre = new_center; + // substitute that into the ellipse equation, making it quadratic in x + Coord I = A + B*q + C*q*q; // x^2 terms + Coord J = B*r + C*2*q*r + D + E*q; // x^1 terms + Coord K = C*r*r + E*r + F; // x^0 terms + std::vector<Coord> xs = solve_quadratic(I, J, K); - return e; + for (unsigned i = 0; i < xs.size(); ++i) { + Point p(xs[i], q*xs[i] + r); + result.push_back(ShapeIntersection(atan2(p * iuct), line.timeAt(p), p)); + } + return result; } -Ellipse::Ellipse(Geom::Circle const &c) +std::vector<ShapeIntersection> Ellipse::intersect(LineSegment const &seg) const +{ + // we simply re-use the procedure for lines and filter out + // results where the line time value is outside of the unit interval. + std::vector<ShapeIntersection> result = intersect(Line(seg)); + filter_line_segment_intersections(result); + return result; +} + +std::vector<ShapeIntersection> Ellipse::intersect(Ellipse const &other) const +{ + // handle degenerate cases first + if (ray(X) == 0 || ray(Y) == 0) { + + } + // intersection of two ellipses can be solved analytically. + // http://maptools.home.comcast.net/~maptools/BivariateQuadratics.pdf + + Coord A, B, C, D, E, F; + Coord a, b, c, d, e, f; + + // NOTE: the order of coefficients is different to match the convention in the PDF above + // Ax^2 + Bx^2 + Cx + Dy + Exy + F + this->coefficients(A, E, B, C, D, F); + other.coefficients(a, e, b, c, d, f); + + // Assume that Q is the ellipse equation given by uppercase letters + // and R is the equation given by lowercase ones. An intersection exists when + // there is a coefficient mu such that + // mu Q + R = 0 + // + // This can be written in the following way: + // + // | ff cc/2 dd/2 | |1| + // mu Q + R = [1 x y] | cc/2 aa ee/2 | |x| = 0 + // | dd/2 ee/2 bb | |y| + // + // where aa = mu A + a and so on. The determinant can be explicitly written out, + // giving an equation which is cubic in mu and can be solved analytically. + + Coord I, J, K, L; + I = (-E*E*F + 4*A*B*F + C*D*E - A*D*D - B*C*C) / 4; + J = -((E*E - 4*A*B) * f + (2*E*F - C*D) * e + (2*A*D - C*E) * d + + (2*B*C - D*E) * c + (C*C - 4*A*F) * b + (D*D - 4*B*F) * a) / 4; + K = -((e*e - 4*a*b) * F + (2*e*f - c*d) * E + (2*a*d - c*e) * D + + (2*b*c - d*e) * C + (c*c - 4*a*f) * B + (d*d - 4*b*f) * A) / 4; + L = (-e*e*f + 4*a*b*f + c*d*e - a*d*d - b*c*c) / 4; + + std::vector<Coord> mus = solve_cubic(I, J, K, L); + Coord mu = infinity(); + std::vector<ShapeIntersection> result; + + // Now that we have solved for mu, we need to check whether the conic + // determined by mu Q + R is reducible to a product of two lines. If it's not, + // it means that there are no intersections. If it is, the intersections of these + // lines with the original ellipses (if there are any) give the coordinates + // of intersections. + + // Prefer middle root if there are three. + // Out of three possible pairs of lines that go through four points of intersection + // of two ellipses, this corresponds to cross-lines. These intersect the ellipses + // at less shallow angles than the other two options. + if (mus.size() == 3) { + std::swap(mus[1], mus[0]); + } + for (unsigned i = 0; i < mus.size(); ++i) { + Coord aa = mus[i] * A + a; + Coord bb = mus[i] * B + b; + Coord ee = mus[i] * E + e; + Coord delta = ee*ee - 4*aa*bb; + if (delta < 0) continue; + mu = mus[i]; + break; + } + + // if no suitable mu was found, there are no intersections + if (mu == infinity()) return result; + + Coord aa = mu * A + a; + Coord bb = mu * B + b; + Coord cc = mu * C + c; + Coord dd = mu * D + d; + Coord ee = mu * E + e; + Coord ff = mu * F + f; + + Line lines[2]; + + if (aa != 0) { + bb /= aa; cc /= aa; dd /= aa; ee /= aa; /*ff /= aa;*/ + Coord s = (ee + std::sqrt(ee*ee - 4*bb)) / 2; + Coord q = ee - s; + Coord alpha = (dd - cc*q) / (s - q); + Coord beta = cc - alpha; + + lines[0] = Line(1, q, alpha); + lines[1] = Line(1, s, beta); + } else if (bb != 0) { + cc /= bb; /*dd /= bb;*/ ee /= bb; ff /= bb; + Coord s = ee; + Coord q = 0; + Coord alpha = cc / ee; + Coord beta = ff * ee / cc; + + lines[0] = Line(q, 1, alpha); + lines[1] = Line(s, 1, beta); + } else { + lines[0] = Line(ee, 0, dd); + lines[1] = Line(0, 1, cc/ee); + } + + // intersect with the obtained lines and report intersections + for (unsigned li = 0; li < 2; ++li) { + std::vector<ShapeIntersection> as = intersect(lines[li]); + std::vector<ShapeIntersection> bs = other.intersect(lines[li]); + + if (!as.empty() && as.size() == bs.size()) { + for (unsigned i = 0; i < as.size(); ++i) { + ShapeIntersection ix(as[i].first, bs[i].first, + middle_point(as[i].point(), bs[i].point())); + result.push_back(ix); + } + } + } + return result; +} + +std::vector<ShapeIntersection> Ellipse::intersect(D2<Bezier> const &b) const +{ + Coord A, B, C, D, E, F; + coefficients(A, B, C, D, E, F); + + Bezier x = A*b[X]*b[X] + B*b[X]*b[Y] + C*b[Y]*b[Y] + D*b[X] + E*b[Y] + F; + std::vector<Coord> r = x.roots(); + + std::vector<ShapeIntersection> result; + for (unsigned i = 0; i < r.size(); ++i) { + Point p = b.valueAt(r[i]); + result.push_back(ShapeIntersection(timeAt(p), r[i], p)); + } + return result; +} + +bool Ellipse::operator==(Ellipse const &other) const +{ + if (_center != other._center) return false; + + Ellipse a = this->canonicalForm(); + Ellipse b = other.canonicalForm(); + + if (a._rays != b._rays) return false; + if (a._angle != b._angle) return false; + + return true; +} + + +bool are_near(Ellipse const &a, Ellipse const &b, Coord precision) +{ + // We want to know whether no point on ellipse a is further than precision + // from the corresponding point on ellipse b. To check this, we compute + // the four extreme points at the end of each ray for each ellipse + // and check whether they are sufficiently close. + + // First, we need to correct the angles on the ellipses, so that they are + // no further than M_PI/4 apart. This can always be done by rotating + // and exchanging axes. + Ellipse ac = a, bc = b; + if (distance(ac.rotationAngle(), bc.rotationAngle()).radians0() >= M_PI/2) { + ac.setRotationAngle(ac.rotationAngle() + M_PI); + } + if (distance(ac.rotationAngle(), bc.rotationAngle()) >= M_PI/4) { + Angle d1 = distance(ac.rotationAngle() + M_PI/2, bc.rotationAngle()); + Angle d2 = distance(ac.rotationAngle() - M_PI/2, bc.rotationAngle()); + Coord adj = d1.radians0() < d2.radians0() ? M_PI/2 : -M_PI/2; + ac.setRotationAngle(ac.rotationAngle() + adj); + ac.setRays(ac.ray(Y), ac.ray(X)); + } + + // Do the actual comparison by computing four points on each ellipse. + Point tps[] = {Point(1,0), Point(0,1), Point(-1,0), Point(0,-1)}; + for (unsigned i = 0; i < 4; ++i) { + if (!are_near(tps[i] * ac.unitCircleTransform(), + tps[i] * bc.unitCircleTransform(), + precision)) + return false; + } + return true; +} + +std::ostream &operator<<(std::ostream &out, Ellipse const &e) { - m_centre = c.center(); - m_ray[X] = m_ray[Y] = c.ray(); + out << "Ellipse(" << e.center() << ", " << e.rays() + << ", " << format_coord_nice(e.rotationAngle()) << ")"; + return out; } } // end namespace Geom diff --git a/src/2geom/ellipse.h b/src/2geom/ellipse.h index c971c6065..4b1701cce 100644 --- a/src/2geom/ellipse.h +++ b/src/2geom/ellipse.h @@ -1,9 +1,9 @@ -/** - * \file - * \brief Ellipse Curve - * +/** @file + * @brief Ellipse shape + *//* * Authors: - * Marco Cecchetti <mrcekets at gmail.com> + * Marco Cecchetti <mrcekets at gmail.com> + * Krzysztof Kosiński <tweenk.pl@gmail.com> * * Copyright 2008 authors * @@ -32,104 +32,211 @@ */ -#ifndef _2GEOM_ELLIPSE_H_ -#define _2GEOM_ELLIPSE_H_ +#ifndef LIB2GEOM_SEEN_ELLIPSE_H +#define LIB2GEOM_SEEN_ELLIPSE_H #include <vector> -#include <2geom/point.h> +#include <2geom/angle.h> +#include <2geom/bezier-curve.h> #include <2geom/exception.h> -#include <2geom/affine.h> +#include <2geom/forward.h> +#include <2geom/line.h> +#include <2geom/transforms.h> -namespace Geom -{ +namespace Geom { class EllipticalArc; class Circle; +/** @brief Set of points with a constant sum of distances from two foci. + * + * An ellipse can be specified in several ways. Internally, 2Geom uses + * the SVG style representation: center, rays and angle between the +X ray + * and the +X axis. Another popular way is to use an implicit equation, + * which is as follows: + * \f$Ax^2 + Bxy + Cy^2 + Dx + Ey + F = 0\f$ + * + * @ingroup Shapes */ class Ellipse + : boost::multipliable< Ellipse, Translate + , boost::multipliable< Ellipse, Scale + , boost::multipliable< Ellipse, Rotate + , boost::multipliable< Ellipse, Zoom + , boost::multipliable< Ellipse, Affine + , boost::equality_comparable< Ellipse + > > > > > > { - public: - Ellipse(): - m_centre(), - m_ray(), - m_angle(0) + Point _center; + Point _rays; + Angle _angle; +public: + Ellipse() {} + Ellipse(Point const &c, Point const &r, Coord angle) + : _center(c) + , _rays(r) + , _angle(angle) {} - - Ellipse(double cx, double cy, double rx, double ry, double a) - : m_centre(cx, cy), m_ray(rx, ry), m_angle(a) - { + Ellipse(Coord cx, Coord cy, Coord rx, Coord ry, Coord angle) + : _center(cx, cy) + , _rays(rx, ry) + , _angle(angle) + {} + Ellipse(double A, double B, double C, double D, double E, double F) { + setCoefficients(A, B, C, D, E, F); } + /// Construct ellipse from a circle. + Ellipse(Geom::Circle const &c); - // build an ellipse by its implicit equation: - // Ax^2 + Bxy + Cy^2 + Dx + Ey + F = 0 - Ellipse(double A, double B, double C, double D, double E, double F) - { - set(A, B, C, D, E, F); + /// Set center, rays and angle. + void set(Point const &c, Point const &r, Coord angle) { + _center = c; + _rays = r; + _angle = angle; } - - Ellipse(std::vector<Point> const& points) - { - set(points); + /// Set center, rays and angle as constituent values. + void set(Coord cx, Coord cy, Coord rx, Coord ry, Coord a) { + _center[X] = cx; + _center[Y] = cy; + _rays[X] = rx; + _rays[Y] = ry; + _angle = a; } - - Ellipse(Geom::Circle const &c); - - void set(double cx, double cy, double rx, double ry, double a) - { - m_centre[X] = cx; - m_centre[Y] = cy; - m_ray[X] = rx; - m_ray[Y] = ry; - m_angle = a; + /// Set an ellipse by solving its implicit equation. + void setCoefficients(double A, double B, double C, double D, double E, double F); + /// Set the center. + void setCenter(Point const &p) { _center = p; } + /// Set the center by coordinates. + void setCenter(Coord cx, Coord cy) { _center[X] = cx; _center[Y] = cy; } + /// Set both rays of the ellipse. + void setRays(Point const &p) { _rays = p; } + /// Set both rays of the ellipse as coordinates. + void setRays(Coord x, Coord y) { _rays[X] = x; _rays[Y] = y; } + /// Set one of the rays of the ellipse. + void setRay(Coord r, Dim2 d) { _rays[d] = r; } + /// Set the angle the X ray makes with the +X axis. + void setRotationAngle(Angle a) { _angle = a; } + + Point center() const { return _center; } + Coord center(Dim2 d) const { return _center[d]; } + /// Get both rays as a point. + Point rays() const { return _rays; } + /// Get one ray of the ellipse. + Coord ray(Dim2 d) const { return _rays[d]; } + /// Get the angle the X ray makes with the +X axis. + Angle rotationAngle() const { return _angle; } + /// Get the point corresponding to the +X ray of the ellipse. + Point initialPoint() const; + /// Get the point corresponding to the +X ray of the ellipse. + Point finalPoint() const { return initialPoint(); } + + /** @brief Create an ellipse passing through the specified points + * At least five points have to be specified. */ + void fit(std::vector<Point> const& points); + + /** @brief Create an elliptical arc from a section of the ellipse. + * This is mainly useful to determine the flags of the new arc. + * The passed points should lie on the ellipse, otherwise the results + * will be undefined. + * @param ip Initial point of the arc + * @param inner Point in the middle of the arc, used to pick one of two possibilities + * @param fp Final point of the arc + * @return Newly allocated arc, delete when no longer used */ + EllipticalArc *arc(Point const &ip, Point const &inner, Point const &fp); + + /** @brief Return an ellipse with less degrees of freedom. + * The canonical form always has the angle less than \f$\frac{\pi}{2}\f$, + * and zero if the rays are equal (i.e. the ellipse is a circle). */ + Ellipse canonicalForm() const; + void makeCanonical(); + + /** @brief Compute the transform that maps the unit circle to this ellipse. + * Each ellipse can be interpreted as a translated, scaled and rotate unit circle. + * This function returns the transform that maps the unit circle to this ellipse. + * @return Transform from unit circle to the ellipse */ + Affine unitCircleTransform() const; + /** @brief Compute the transform that maps this ellipse to the unit circle. + * This may be a little more precise and/or faster than simply using + * unitCircleTransform().inverse(). An exception will be thrown for + * degenerate ellipses. */ + Affine inverseUnitCircleTransform() const; + + LineSegment majorAxis() const { return ray(X) >= ray(Y) ? axis(X) : axis(Y); } + LineSegment minorAxis() const { return ray(X) < ray(Y) ? axis(X) : axis(Y); } + LineSegment semimajorAxis(int sign = 1) const { + return ray(X) >= ray(Y) ? semiaxis(X, sign) : semiaxis(Y, sign); } - - // build an ellipse by its implicit equation: - // Ax^2 + Bxy + Cy^2 + Dx + Ey + F = 0 - void set(double A, double B, double C, double D, double E, double F); - - // biuld up the best fitting ellipse wrt the passed points - // prerequisite: at least 5 points must be passed - void set(std::vector<Point> const& points); - - EllipticalArc * - arc(Point const& initial, Point const& inner, Point const& final, bool svg_compliant = true); - - Point center() const - { - return m_centre; + LineSegment semiminorAxis(int sign = 1) const { + return ray(X) < ray(Y) ? semiaxis(X, sign) : semiaxis(Y, sign); } - - Coord center(Dim2 d) const - { - return m_centre[d]; + LineSegment axis(Dim2 d) const; + LineSegment semiaxis(Dim2 d, int sign = 1) const; + + /// Get the coefficients of the ellipse's implicit equation. + std::vector<double> coefficients() const; + void coefficients(Coord &A, Coord &B, Coord &C, Coord &D, Coord &E, Coord &F) const; + + /** @brief Evaluate a point on the ellipse. + * The parameter range is \f$[0, 2\pi)\f$; larger and smaller values + * wrap around. */ + Point pointAt(Coord t) const; + /// Evaluate a single coordinate of a point on the ellipse. + Coord valueAt(Coord t, Dim2 d) const; + + /** @brief Find the time value of a point on an ellipse. + * If the point is not on the ellipse, the returned time value will correspond + * to an intersection with a ray from the origin passing through the point + * with the ellipse. Note that this is NOT the nearest point on the ellipse. */ + Coord timeAt(Point const &p) const; + + /// Get the value of the derivative at time t normalized to unit length. + Point unitTangentAt(Coord t) const; + + /// Check whether the ellipse contains the given point. + bool contains(Point const &p) const; + + /// Compute intersections with an infinite line. + std::vector<ShapeIntersection> intersect(Line const &line) const; + /// Compute intersections with a line segment. + std::vector<ShapeIntersection> intersect(LineSegment const &seg) const; + /// Compute intersections with another ellipse. + std::vector<ShapeIntersection> intersect(Ellipse const &other) const; + /// Compute intersections with a 2D Bezier polynomial. + std::vector<ShapeIntersection> intersect(D2<Bezier> const &other) const; + + Ellipse &operator*=(Translate const &t) { + _center *= t; + return *this; } - - Coord ray(Dim2 d) const - { - return m_ray[d]; + Ellipse &operator*=(Scale const &s) { + _center *= s; + _rays *= s; + return *this; } - - Coord rot_angle() const - { - return m_angle; + Ellipse &operator*=(Zoom const &z) { + _center *= z; + _rays *= z.scale(); + return *this; } + Ellipse &operator*=(Rotate const &r); + Ellipse &operator*=(Affine const &m); - std::vector<double> implicit_form_coefficients() const; - - Ellipse transformed(Affine const& m) const; - - private: - Point m_centre, m_ray; - double m_angle; + /// Compare ellipses for exact equality. + bool operator==(Ellipse const &other) const; }; +/** @brief Test whether two ellipses are approximately the same. + * This will check whether no point on ellipse a is further away from + * the corresponding point on ellipse b than precision. + * @relates Ellipse */ +bool are_near(Ellipse const &a, Ellipse const &b, Coord precision = EPSILON); -} // end namespace Geom - +/** @brief Outputs ellipse data, useful for debugging. + * @relates Ellipse */ +std::ostream &operator<<(std::ostream &out, Ellipse const &e); +} // end namespace Geom -#endif // _2GEOM_ELLIPSE_H_ - +#endif // LIB2GEOM_SEEN_ELLIPSE_H /* Local Variables: diff --git a/src/2geom/svg-elliptical-arc.cpp b/src/2geom/elliptical-arc-from-sbasis.cpp index 96a4f99d8..54f995fb6 100644 --- a/src/2geom/svg-elliptical-arc.cpp +++ b/src/2geom/elliptical-arc-from-sbasis.cpp @@ -1,6 +1,8 @@ -/* - * SVG Elliptical Arc Class +/** @file + * @brief Fitting elliptical arc to SBasis * + * This file contains the implementation of the function arc_from_sbasis. + *//* * Copyright 2008 Marco Cecchetti <mrcekets at gmail.com> * * This library is free software; you can redistribute it and/or @@ -27,33 +29,120 @@ * the specific language governing rights and limitations. */ - +#include <2geom/curve.h> +#include <2geom/angle.h> +#include <2geom/utils.h> #include <2geom/bezier-curve.h> -#include <2geom/ellipse.h> -#include <2geom/numeric/fitting-model.h> -#include <2geom/numeric/fitting-tool.h> +#include <2geom/elliptical-arc.h> +#include <2geom/sbasis-curve.h> // for non-native methods #include <2geom/numeric/vector.h> -#include <2geom/poly.h> -#include <2geom/sbasis-geometric.h> -#include <2geom/svg-elliptical-arc.h> - -#include <cfloat> -#include <limits> -#include <memory> +#include <2geom/numeric/fitting-tool.h> +#include <2geom/numeric/fitting-model.h> +#include <algorithm> +namespace Geom { -namespace Geom +// forward declation +namespace detail { + struct ellipse_equation; +} -/** - * @class SVGEllipticalArc - * @brief SVG 1.1-compliant elliptical arc. +/* + * make_elliptical_arc * - * This class is almost identical to the normal elliptical arc, but it differs slightly - * in the handling of degenerate arcs to be compliant with SVG 1.1 implementation guidelines. + * convert a parametric polynomial curve given in symmetric power basis form + * into an EllipticalArc type; in order to be successfull the input curve + * has to look like an actual elliptical arc even if a certain tolerance + * is allowed through an ad-hoc parameter. + * The conversion is performed through an interpolation on a certain amount of + * sample points computed on the input curve; + * the interpolation computes the coefficients of the general implicit equation + * of an ellipse (A*X^2 + B*XY + C*Y^2 + D*X + E*Y + F = 0), then from the + * implicit equation we compute the parametric form. * - * @ingroup Curves */ +class make_elliptical_arc +{ + public: + typedef D2<SBasis> curve_type; + + /* + * constructor + * + * it doesn't execute the conversion but set the input and output parameters + * + * _ea: the output EllipticalArc that will be generated; + * _curve: the input curve to be converted; + * _total_samples: the amount of sample points to be taken + * on the input curve for performing the conversion + * _tolerance: how much likelihood is required between the input curve + * and the generated elliptical arc; the smaller it is the + * the tolerance the higher it is the likelihood. + */ + make_elliptical_arc( EllipticalArc& _ea, + curve_type const& _curve, + unsigned int _total_samples, + double _tolerance ); + + private: + bool bound_exceeded( unsigned int k, detail::ellipse_equation const & ee, + double e1x, double e1y, double e2 ); + + bool check_bound(double A, double B, double C, double D, double E, double F); + + void fit(); + + bool make_elliptiarc(); + + void print_bound_error(unsigned int k) + { + std::cerr + << "tolerance error" << std::endl + << "at point: " << k << std::endl + << "error value: "<< dist_err << std::endl + << "bound: " << dist_bound << std::endl + << "angle error: " << angle_err + << " (" << angle_tol << ")" << std::endl; + } + + public: + /* + * perform the actual conversion + * return true if the conversion is successfull, false on the contrary + */ + bool operator()() + { + // initialize the reference + const NL::Vector & coeff = fitter.result(); + fit(); + if ( !check_bound(1, coeff[0], coeff[1], coeff[2], coeff[3], coeff[4]) ) + return false; + if ( !(make_elliptiarc()) ) return false; + return true; + } + + private: + EllipticalArc& ea; // output elliptical arc + const curve_type & curve; // input curve + Piecewise<D2<SBasis> > dcurve; // derivative of the input curve + NL::LFMEllipse model; // model used for fitting + // perform the actual fitting task + NL::least_squeares_fitter<NL::LFMEllipse> fitter; + // tolerance: the user-defined tolerance parameter; + // tol_at_extr: the tolerance at end-points automatically computed + // on the value of "tolerance", and usually more strict; + // tol_at_center: tolerance at the center of the ellipse + // angle_tol: tolerance for the angle btw the input curve tangent + // versor and the ellipse normal versor at the sample points + double tolerance, tol_at_extr, tol_at_center, angle_tol; + Point initial_point, final_point; // initial and final end-points + unsigned int N; // total samples + unsigned int last; // N-1 + double partitions; // N-1 + std::vector<Point> p; // sample points + double dist_err, dist_bound, angle_err; +}; namespace detail { @@ -97,7 +186,7 @@ struct ellipse_equation double A, B, C, D, E, F; }; -} +} // end namespace detail make_elliptical_arc:: make_elliptical_arc( EllipticalArc& _ea, @@ -110,8 +199,7 @@ make_elliptical_arc( EllipticalArc& _ea, tolerance(_tolerance), tol_at_extr(tolerance/2), tol_at_center(0.1), angle_tol(0.1), initial_point(curve.at0()), final_point(curve.at1()), - N(_total_samples), last(N-1), partitions(N-1), p(N), - svg_compliant(true) + N(_total_samples), last(N-1), partitions(N-1), p(N) { } @@ -207,7 +295,7 @@ bool make_elliptical_arc::make_elliptiarc() Ellipse e; try { - e.set(1, coeff[0], coeff[1], coeff[2], coeff[3], coeff[4]); + e.setCoefficients(1, coeff[0], coeff[1], coeff[2], coeff[3], coeff[4]); } catch(LogicalError const &exc) { @@ -216,33 +304,12 @@ bool make_elliptical_arc::make_elliptiarc() Point inner_point = curve(0.5); - if (svg_compliant_flag()) - { -#ifdef CPP11 - std::unique_ptr<EllipticalArc> arc( e.arc(initial_point, inner_point, final_point, true) ); -#else - std::auto_ptr<EllipticalArc> arc( e.arc(initial_point, inner_point, final_point, true) ); -#endif - ea = *arc; - } - else - { - try - { #ifdef CPP11 - std::unique_ptr<EllipticalArc> + std::unique_ptr<EllipticalArc> arc( e.arc(initial_point, inner_point, final_point) ); #else - std::auto_ptr<EllipticalArc> + std::auto_ptr<EllipticalArc> arc( e.arc(initial_point, inner_point, final_point) ); #endif - eap( e.arc(initial_point, inner_point, final_point, false) ); - ea = *eap; - } - catch(RangeError const &exc) - { - return false; - } - } - + ea = *arc; if ( !are_near( e.center(), ea.center(), @@ -257,10 +324,14 @@ bool make_elliptical_arc::make_elliptiarc() -} // end namespace Geom - - +bool arc_from_sbasis(EllipticalArc &ea, D2<SBasis> const &in, + double tolerance, unsigned num_samples) +{ + make_elliptical_arc convert(ea, in, num_samples, tolerance); + return convert(); +} +} // end namespace Geom /* Local Variables: @@ -272,4 +343,3 @@ bool make_elliptical_arc::make_elliptiarc() End: */ // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : - diff --git a/src/2geom/elliptical-arc.cpp b/src/2geom/elliptical-arc.cpp index c96d5f1a6..25a78b4ad 100644 --- a/src/2geom/elliptical-arc.cpp +++ b/src/2geom/elliptical-arc.cpp @@ -34,11 +34,11 @@ #include <limits> #include <memory> -#include <2geom/elliptical-arc.h> +#include <2geom/bezier-curve.h> #include <2geom/ellipse.h> +#include <2geom/elliptical-arc.h> +#include <2geom/path-sink.h> #include <2geom/sbasis-geometric.h> -#include <2geom/bezier-curve.h> -#include <2geom/poly.h> #include <2geom/transforms.h> #include <2geom/utils.h> @@ -46,7 +46,6 @@ #include <2geom/numeric/fitting-tool.h> #include <2geom/numeric/fitting-model.h> - namespace Geom { @@ -84,18 +83,27 @@ namespace Geom * and the ellipse's rotation. Each set of those parameters corresponds to four different arcs, * with two of them larger than half an ellipse and two of them turning clockwise while traveling * from initial to final point. The two flags disambiguate between them: "large arc flag" selects - * the bigger arc, while the "sweep flag" selects the clockwise arc. + * the bigger arc, while the "sweep flag" selects the arc going in the direction of positive + * angles. Angles always increase when going from the +X axis in the direction of the +Y axis, + * so if Y grows downwards, this means clockwise. * - * @image html elliptical-arc-flags.png "The four possible arcs and the meaning of flags" + * @image html elliptical-arc-flags.png "Meaning of arc flags (Y grows downwards)" * * @ingroup Curves */ Rect EllipticalArc::boundsExact() const { + if (isChord()) { + return chord().boundsExact(); + } + + using std::swap; + + // TODO: simplify / document what is going on here. double extremes[4]; double sinrot, cosrot; - sincos(_rot_angle, sinrot, cosrot); + sincos(rotationAngle(), sinrot, cosrot); extremes[0] = std::atan2( -ray(Y) * sinrot, ray(X) * cosrot ); extremes[1] = extremes[0] + M_PI; @@ -109,11 +117,11 @@ Rect EllipticalArc::boundsExact() const arc_extremes[0] = initialPoint()[X]; arc_extremes[1] = finalPoint()[X]; if ( arc_extremes[0] < arc_extremes[1] ) - std::swap(arc_extremes[0], arc_extremes[1]); + swap(arc_extremes[0], arc_extremes[1]); arc_extremes[2] = initialPoint()[Y]; arc_extremes[3] = finalPoint()[Y]; if ( arc_extremes[2] < arc_extremes[3] ) - std::swap(arc_extremes[2], arc_extremes[3]); + swap(arc_extremes[2], arc_extremes[3]); if ( !are_near(initialPoint(), finalPoint()) ) { for (unsigned i = 0; i < 4; ++i) { @@ -130,37 +138,23 @@ Rect EllipticalArc::boundsExact() const Point EllipticalArc::pointAtAngle(Coord t) const { - Point ret = Point::polar(t) * unitCircleTransform(); + Point ret = _ellipse.pointAt(t); return ret; } Coord EllipticalArc::valueAtAngle(Coord t, Dim2 d) const { - Coord sinrot, cosrot, cost, sint; - sincos(_rot_angle, sinrot, cosrot); - sincos(t, sint, cost); - - if ( d == X ) { - return ray(X) * cosrot * cost - - ray(Y) * sinrot * sint - + center(X); - } else { - return ray(X) * sinrot * cost - + ray(Y) * cosrot * sint - + center(Y); - } -} - -Affine EllipticalArc::unitCircleTransform() const -{ - Affine ret = Scale(ray(X), ray(Y)) * Rotate(_rot_angle); - ret.setTranslation(center()); - return ret; + return _ellipse.valueAt(t, d); } std::vector<Coord> EllipticalArc::roots(Coord v, Dim2 d) const { + if (isChord()) { + return chord().roots(v, d); + } + std::vector<Coord> sol; + Interval unit_interval(0, 1); if ( are_near(ray(X), 0) && are_near(ray(Y), 0) ) { if ( center(d) == v ) @@ -188,7 +182,7 @@ std::vector<Coord> EllipticalArc::roots(Coord v, Dim2 d) const for ( unsigned int dim = 0; dim < 2; ++dim ) { - if ( are_near(ray((Dim2) dim), 0) ) + if (ray((Dim2) dim) == 0) { if ( initialPoint()[d] == v && finalPoint()[d] == v ) { @@ -210,18 +204,18 @@ std::vector<Coord> EllipticalArc::roots(Coord v, Dim2 d) const case X: switch(dim) { - case X: ray_prj = -ray(Y) * std::sin(_rot_angle); + case X: ray_prj = -ray(Y) * std::sin(rotationAngle()); break; - case Y: ray_prj = ray(X) * std::cos(_rot_angle); + case Y: ray_prj = ray(X) * std::cos(rotationAngle()); break; } break; case Y: switch(dim) { - case X: ray_prj = ray(Y) * std::cos(_rot_angle); + case X: ray_prj = ray(Y) * std::cos(rotationAngle()); break; - case Y: ray_prj = ray(X) * std::sin(_rot_angle); + case Y: ray_prj = ray(X) * std::sin(rotationAngle()); break; } break; @@ -236,7 +230,7 @@ std::vector<Coord> EllipticalArc::roots(Coord v, Dim2 d) const { case X: s = std::asin(s); // return a value in [-PI/2,PI/2] - if ( logical_xor( _sweep, are_near(initialAngle(), M_PI/2) ) ) + if ( logical_xor( sweep(), are_near(initialAngle(), M_PI/2) ) ) { if ( s < 0 ) s += 2*M_PI; } @@ -248,7 +242,7 @@ std::vector<Coord> EllipticalArc::roots(Coord v, Dim2 d) const break; case Y: s = std::acos(s); // return a value in [0,PI] - if ( logical_xor( _sweep, are_near(initialAngle(), 0) ) ) + if ( logical_xor( sweep(), are_near(initialAngle(), 0) ) ) { s = 2*M_PI - s; if ( !(s < 2*M_PI) ) s -= 2*M_PI; @@ -257,17 +251,22 @@ std::vector<Coord> EllipticalArc::roots(Coord v, Dim2 d) const } //std::cerr << "s = " << rad_to_deg(s); - s = map_to_01(s); + s = timeAtAngle(s); //std::cerr << " -> t: " << s << std::endl; - if ( !(s < 0 || s > 1) ) + if (unit_interval.contains(s)) { sol.push_back(s); + } return sol; } } double rotx, roty; - sincos(_rot_angle, roty, rotx); /// \todo sin and cos are calculated in many places in this function, optimize this a bit! - if (d == X) roty = -roty; + if (d == X) { + sincos(rotationAngle(), roty, rotx); + roty = -roty; + } else { + sincos(rotationAngle(), rotx, roty); + } double rxrotx = ray(X) * rotx; double c_v = center(d) - v; @@ -312,13 +311,13 @@ std::vector<Coord> EllipticalArc::roots(Coord v, Dim2 d) const } std::vector<double> arc_sol; - for (unsigned int i = 0; i < sol.size(); ++i ) - { + for (unsigned int i = 0; i < sol.size(); ++i ) { //std::cerr << "s = " << rad_to_deg(sol[i]); - sol[i] = map_to_01(sol[i]); + sol[i] = timeAtAngle(sol[i]); //std::cerr << " -> t: " << sol[i] << std::endl; - if ( !(sol[i] < 0 || sol[i] > 1) ) + if (unit_interval.contains(sol[i])) { arc_sol.push_back(sol[i]); + } } return arc_sol; } @@ -330,18 +329,14 @@ std::vector<Coord> EllipticalArc::roots(Coord v, Dim2 d) const // of such an angle in the cw direction Curve *EllipticalArc::derivative() const { - EllipticalArc *result = static_cast<EllipticalArc*>(duplicate()); - result->_center[X] = result->_center[Y] = 0; - result->_start_angle += M_PI/2; - if( !( result->_start_angle < 2*M_PI ) ) - { - result->_start_angle -= 2*M_PI; - } - result->_end_angle += M_PI/2; - if( !( result->_end_angle < 2*M_PI ) ) - { - result->_end_angle -= 2*M_PI; + if (isChord()) { + return chord().derivative(); } + + EllipticalArc *result = static_cast<EllipticalArc*>(duplicate()); + result->_ellipse.setCenter(0, 0); + result->_angles.setInitial(result->_angles.initialAngle() + M_PI/2); + result->_angles.setFinal(result->_angles.finalAngle() + M_PI/2); result->_initial_point = result->pointAtAngle( result->initialAngle() ); result->_final_point = result->pointAtAngle( result->finalAngle() ); return result; @@ -351,18 +346,21 @@ Curve *EllipticalArc::derivative() const std::vector<Point> EllipticalArc::pointAndDerivatives(Coord t, unsigned int n) const { + if (isChord()) { + return chord().pointAndDerivatives(t, n); + } + unsigned int nn = n+1; // nn represents the size of the result vector. std::vector<Point> result; result.reserve(nn); - double angle = map_unit_interval_on_circular_arc(t, initialAngle(), - finalAngle(), _sweep); + double angle = angleAt(t); std::auto_ptr<EllipticalArc> ea( static_cast<EllipticalArc*>(duplicate()) ); - ea->_center = Point(0,0); + ea->_ellipse.setCenter(0, 0); unsigned int m = std::min(nn, 4u); for ( unsigned int i = 0; i < m; ++i ) { result.push_back( ea->pointAtAngle(angle) ); - angle += (_sweep ? M_PI/2 : -M_PI/2); + angle += (sweep() ? M_PI/2 : -M_PI/2); if ( !(angle < 2*M_PI) ) angle -= 2*M_PI; } m = nn / 4; @@ -381,9 +379,16 @@ EllipticalArc::pointAndDerivatives(Coord t, unsigned int n) const return result; } -bool EllipticalArc::containsAngle(Coord angle) const +Point EllipticalArc::pointAt(Coord t) const { - return AngleInterval::contains(angle); + if (isChord()) return chord().pointAt(t); + return _ellipse.pointAt(angleAt(t)); +} + +Coord EllipticalArc::valueAt(Coord t, Dim2 d) const +{ + if (isChord()) return chord().valueAt(t, d); + return valueAtAngle(angleAt(t), d); } Curve* EllipticalArc::portion(double f, double t) const @@ -394,41 +399,35 @@ Curve* EllipticalArc::portion(double f, double t) const if (t < 0) t = 0; if (t > 1) t = 1; - if ( are_near(f, t) ) - { - EllipticalArc *arc = static_cast<EllipticalArc*>(duplicate()); - arc->_center = arc->_initial_point = arc->_final_point = pointAt(f); - arc->_start_angle = arc->_end_angle = _start_angle; - arc->_rot_angle = _rot_angle; - arc->_sweep = _sweep; - arc->_large_arc = _large_arc; + if (f == t) { + EllipticalArc *arc = new EllipticalArc(); + arc->_initial_point = arc->_final_point = pointAt(f); return arc; } EllipticalArc *arc = static_cast<EllipticalArc*>(duplicate()); arc->_initial_point = pointAt(f); arc->_final_point = pointAt(t); - if ( f > t ) arc->_sweep = !_sweep; - if ( _large_arc && fabs(sweepAngle() * (t-f)) < M_PI) + arc->_angles.setAngles(angleAt(f), angleAt(t)); + if (f > t) arc->_angles.setSweep(!sweep()); + if ( _large_arc && fabs(angularExtent() * (t-f)) <= M_PI) { arc->_large_arc = false; - arc->_updateCenterAndAngles(arc->isSVGCompliant()); //TODO: be more clever + } return arc; } // the arc is the same but traversed in the opposite direction -Curve *EllipticalArc::reverse() const { +Curve *EllipticalArc::reverse() const +{ + using std::swap; EllipticalArc *rarc = static_cast<EllipticalArc*>(duplicate()); - rarc->_sweep = !_sweep; - rarc->_initial_point = _final_point; - rarc->_final_point = _initial_point; - rarc->_start_angle = _end_angle; - rarc->_end_angle = _start_angle; - rarc->_updateCenterAndAngles(rarc->isSVGCompliant()); + rarc->_angles.reverse(); + swap(rarc->_initial_point, rarc->_final_point); return rarc; } #ifdef HAVE_GSL // GSL is required for function "solve_reals" -std::vector<double> EllipticalArc::allNearestPoints( Point const& p, double from, double to ) const +std::vector<double> EllipticalArc::allNearestTimes( Point const& p, double from, double to ) const { std::vector<double> result; @@ -446,11 +445,11 @@ std::vector<double> EllipticalArc::allNearestPoints( Point const& p, double from else if ( are_near(ray(X), 0) || are_near(ray(Y), 0) ) { LineSegment seg(pointAt(from), pointAt(to)); - Point np = seg.pointAt( seg.nearestPoint(p) ); + Point np = seg.pointAt( seg.nearestTime(p) ); if ( are_near(ray(Y), 0) ) { - if ( are_near(_rot_angle, M_PI/2) - || are_near(_rot_angle, 3*M_PI/2) ) + if ( are_near(rotationAngle(), M_PI/2) + || are_near(rotationAngle(), 3*M_PI/2) ) { result = roots(np[Y], Y); } @@ -461,8 +460,8 @@ std::vector<double> EllipticalArc::allNearestPoints( Point const& p, double from } else { - if ( are_near(_rot_angle, M_PI/2) - || are_near(_rot_angle, 3*M_PI/2) ) + if ( are_near(rotationAngle(), M_PI/2) + || are_near(rotationAngle(), 3*M_PI/2) ) { result = roots(np[X], X); } @@ -521,7 +520,7 @@ std::vector<double> EllipticalArc::allNearestPoints( Point const& p, double from Point p_c = p - center(); double rx2_ry2 = (ray(X) - ray(Y)) * (ray(X) + ray(Y)); double sinrot, cosrot; - sincos(_rot_angle, sinrot, cosrot); + sincos(rotationAngle(), sinrot, cosrot); double expr1 = ray(X) * (p_c[X] * cosrot + p_c[Y] * sinrot); Poly coeff; coeff.resize(5); @@ -570,7 +569,7 @@ std::vector<double> EllipticalArc::allNearestPoints( Point const& p, double from double mindistsq1 = std::numeric_limits<double>::max(); double mindistsq2 = std::numeric_limits<double>::max(); - double dsq; + double dsq = 0; unsigned int mi1 = 0, mi2 = 0; for ( unsigned int i = 0; i < real_sol.size(); ++i ) { @@ -589,14 +588,14 @@ std::vector<double> EllipticalArc::allNearestPoints( Point const& p, double from } } - double t = map_to_01( real_sol[mi1] ); + double t = timeAtAngle(real_sol[mi1]); if ( !(t < from || t > to) ) { result.push_back(t); } bool second_sol = false; - t = map_to_01( real_sol[mi2] ); + t = timeAtAngle(real_sol[mi2]); if ( real_sol.size() == 4 && !(t < from || t > to) ) { if ( result.empty() || are_near(mindistsq1, mindistsq2) ) @@ -656,227 +655,155 @@ std::vector<double> EllipticalArc::allNearestPoints( Point const& p, double from } #endif -/* - * NOTE: this implementation follows Standard SVG 1.1 implementation guidelines - * for elliptical arc curves. See Appendix F.6. - */ -void EllipticalArc::_updateCenterAndAngles(bool svg) -{ - Point d = initialPoint() - finalPoint(); - // TODO move this to SVGElipticalArc? - if (svg) - { - if ( initialPoint() == finalPoint() ) - { - _rot_angle = _start_angle = _end_angle = 0; - _center = initialPoint(); - _rays = Geom::Point(0,0); - _large_arc = _sweep = false; - return; +void EllipticalArc::_filterIntersections(std::vector<ShapeIntersection> &xs, bool is_first) const +{ + Interval unit(0, 1); + std::vector<ShapeIntersection>::reverse_iterator i = xs.rbegin(), last = xs.rend(); + while (i != last) { + Coord &t = is_first ? i->first : i->second; + assert(are_near(_ellipse.pointAt(t), i->point(), 1e-6)); + t = timeAtAngle(t); + if (!unit.contains(t)) { + xs.erase((++i).base()); + continue; + } else { + assert(are_near(pointAt(t), i->point(), 1e-6)); + ++i; } + } +} - _rays[X] = std::fabs(_rays[X]); - _rays[Y] = std::fabs(_rays[Y]); - - if ( are_near(ray(X), 0) || are_near(ray(Y), 0) ) - { - _rays[X] = L2(d) / 2; - _rays[Y] = 0; - _rot_angle = std::atan2(d[Y], d[X]); - _start_angle = 0; - _end_angle = M_PI; - _center = middle_point(initialPoint(), finalPoint()); - _large_arc = false; - _sweep = false; - return; - } +std::vector<CurveIntersection> EllipticalArc::intersect(Curve const &other, Coord eps) const +{ + if (isLineSegment()) { + LineSegment ls(_initial_point, _final_point); + return ls.intersect(other, eps); } - else - { - if ( are_near(initialPoint(), finalPoint()) ) - { - if ( are_near(ray(X), 0) && are_near(ray(Y), 0) ) - { - _start_angle = _end_angle = 0; - _center = initialPoint(); - return; - } - else - { - THROW_RANGEERROR("initial and final point are the same"); - } - } - if ( are_near(ray(X), 0) && are_near(ray(Y), 0) ) - { // but initialPoint != finalPoint - THROW_RANGEERROR( - "there is no ellipse that satisfies the given constraints: " - "ray(X) == 0 && ray(Y) == 0 but initialPoint != finalPoint" - ); - } - if ( are_near(ray(Y), 0) ) - { - Point v = initialPoint() - finalPoint(); - if ( are_near(L2sq(v), 4*ray(X)*ray(X)) ) - { - Angle angle(v); - if ( are_near( angle, _rot_angle ) ) - { - _start_angle = 0; - _end_angle = M_PI; - _center = v/2 + finalPoint(); - return; - } - angle -= M_PI; - if ( are_near( angle, _rot_angle ) ) - { - _start_angle = M_PI; - _end_angle = 0; - _center = v/2 + finalPoint(); - return; - } - THROW_RANGEERROR( - "there is no ellipse that satisfies the given constraints: " - "ray(Y) == 0 " - "and slope(initialPoint - finalPoint) != rotation_angle " - "and != rotation_angle + PI" - ); - } - if ( L2sq(v) > 4*ray(X)*ray(X) ) - { - THROW_RANGEERROR( - "there is no ellipse that satisfies the given constraints: " - "ray(Y) == 0 and distance(initialPoint, finalPoint) > 2*ray(X)" - ); - } - else - { - THROW_RANGEERROR( - "there is infinite ellipses that satisfy the given constraints: " - "ray(Y) == 0 and distance(initialPoint, finalPoint) < 2*ray(X)" - ); - } - } + std::vector<CurveIntersection> result; - if ( are_near(ray(X), 0) ) - { - Point v = initialPoint() - finalPoint(); - if ( are_near(L2sq(v), 4*ray(Y)*ray(Y)) ) - { - double angle = std::atan2(v[Y], v[X]); - if (angle < 0) angle += 2*M_PI; - double rot_angle = _rot_angle.radians() + M_PI/2; - if ( !(rot_angle < 2*M_PI) ) rot_angle -= 2*M_PI; - if ( are_near( angle, rot_angle ) ) - { - _start_angle = M_PI/2; - _end_angle = 3*M_PI/2; - _center = v/2 + finalPoint(); - return; - } - angle -= M_PI; - if ( angle < 0 ) angle += 2*M_PI; - if ( are_near( angle, rot_angle ) ) - { - _start_angle = 3*M_PI/2; - _end_angle = M_PI/2; - _center = v/2 + finalPoint(); - return; - } - THROW_RANGEERROR( - "there is no ellipse that satisfies the given constraints: " - "ray(X) == 0 " - "and slope(initialPoint - finalPoint) != rotation_angle + PI/2 " - "and != rotation_angle + (3/2)*PI" - ); - } - if ( L2sq(v) > 4*ray(Y)*ray(Y) ) - { - THROW_RANGEERROR( - "there is no ellipse that satisfies the given constraints: " - "ray(X) == 0 and distance(initialPoint, finalPoint) > 2*ray(Y)" - ); - } - else - { - THROW_RANGEERROR( - "there is infinite ellipses that satisfy the given constraints: " - "ray(X) == 0 and distance(initialPoint, finalPoint) < 2*ray(Y)" - ); - } + if (other.isLineSegment()) { + LineSegment ls(other.initialPoint(), other.finalPoint()); + result = _ellipse.intersect(ls); + _filterIntersections(result, true); + return result; + } - } + BezierCurve const *bez = dynamic_cast<BezierCurve const *>(&other); + if (bez) { + result = _ellipse.intersect(bez->fragment()); + _filterIntersections(result, true); + return result; + } + EllipticalArc const *arc = dynamic_cast<EllipticalArc const *>(&other); + if (arc) { + result = _ellipse.intersect(arc->_ellipse); + _filterIntersections(result, true); + arc->_filterIntersections(result, false); + return result; } - Rotate rm(_rot_angle); - Affine m(rm); - m[1] = -m[1]; - m[2] = -m[2]; - - Point p = (d / 2) * m; - double rx2 = _rays[X] * _rays[X]; - double ry2 = _rays[Y] * _rays[Y]; - double rxpy = _rays[X] * p[Y]; - double rypx = _rays[Y] * p[X]; - double rx2py2 = rxpy * rxpy; - double ry2px2 = rypx * rypx; - double num = rx2 * ry2; - double den = rx2py2 + ry2px2; - assert(den != 0); - double rad = num / den; - Point c(0,0); - if (rad > 1) - { - rad -= 1; - rad = std::sqrt(rad); + // in case someone wants to make a custom curve type + result = other.intersect(*this, eps); + transpose_in_place(result); + return result; +} - if (_large_arc == _sweep) rad = -rad; - c = rad * Point(rxpy / ray(Y), -rypx / ray(X)); - _center = c * rm + middle_point(initialPoint(), finalPoint()); - } - else if (rad == 1 || svg) - { - double lamda = std::sqrt(1 / rad); - _rays[X] *= lamda; - _rays[Y] *= lamda; - _center = middle_point(initialPoint(), finalPoint()); - } - else - { - THROW_RANGEERROR( - "there is no ellipse that satisfies the given constraints" - ); +void EllipticalArc::_updateCenterAndAngles() +{ + // See: http://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes + Point d = initialPoint() - finalPoint(); + Point mid = middle_point(initialPoint(), finalPoint()); + + // if ip = sp, the arc contains no other points + if (initialPoint() == finalPoint()) { + _ellipse = Ellipse(); + _ellipse.setCenter(initialPoint()); + _angles = AngleInterval(); + _large_arc = false; + return; + } + + // rays should be positive + _ellipse.setRays(std::fabs(ray(X)), std::fabs(ray(Y))); + + if (isChord()) { + _ellipse.setRays(L2(d) / 2, 0); + _ellipse.setRotationAngle(atan2(d)); + _ellipse.setCenter(mid); + _angles.setAngles(0, M_PI); + _angles.setSweep(false); + _large_arc = false; + return; + } + + Rotate rot(rotationAngle()); // the matrix in F.6.5.3 + Rotate invrot = rot.inverse(); // the matrix in F.6.5.1 + + Point r = rays(); + Point p = (initialPoint() - mid) * invrot; // x', y' in F.6.5.1 + Point c(0,0); // cx', cy' in F.6.5.2 + + // Correct out-of-range radii + Coord lambda = hypot(p[X]/r[X], p[Y]/r[Y]); + if (lambda > 1) { + r *= lambda; + _ellipse.setRays(r); + _ellipse.setCenter(mid); + } else { + // evaluate F.6.5.2 + Coord rxry = r[X]*r[X] * r[Y]*r[Y]; + Coord pxry = p[X]*p[X] * r[Y]*r[Y]; + Coord rxpy = r[X]*r[X] * p[Y]*p[Y]; + Coord rad = (rxry - pxry - rxpy)/(rxpy + pxry); + // normally rad should never be negative, but numerical inaccuracy may cause this + if (rad > 0) { + rad = std::sqrt(rad); + if (sweep() == _large_arc) { + rad = -rad; + } + c = rad * Point(r[X]*p[Y]/r[Y], -r[Y]*p[X]/r[X]); + _ellipse.setCenter(c * rot + mid); + } else { + _ellipse.setCenter(mid); + } } - Point sp((p[X] - c[X]) / ray(X), (p[Y] - c[Y]) / ray(Y)); - Point ep((-p[X] - c[X]) / ray(X), (-p[Y] - c[Y]) / ray(Y)); + // Compute start and end angles. + // If the ellipse was enlarged, c will be zero - this is correct. + Point sp((p[X] - c[X]) / r[X], (p[Y] - c[Y]) / r[Y]); + Point ep((-p[X] - c[X]) / r[X], (-p[Y] - c[Y]) / r[Y]); Point v(1, 0); - _start_angle = angle_between(v, sp); - double sweep_angle = angle_between(sp, ep); - if (!_sweep && sweep_angle > 0) sweep_angle -= 2*M_PI; - if (_sweep && sweep_angle < 0) sweep_angle += 2*M_PI; - _end_angle = _start_angle; - _end_angle += sweep_angle; + _angles.setInitial(angle_between(v, sp)); + _angles.setFinal(angle_between(v, ep)); + + /*double sweep_angle = angle_between(sp, ep); + if (!sweep() && sweep_angle > 0) sweep_angle -= 2*M_PI; + if (sweep() && sweep_angle < 0) sweep_angle += 2*M_PI;*/ } D2<SBasis> EllipticalArc::toSBasis() const { + if (isChord()) { + return chord().toSBasis(); + } + D2<SBasis> arc; // the interval of parametrization has to be [0,1] - Coord et = initialAngle().radians() + ( _sweep ? sweepAngle() : -sweepAngle() ); - Linear param(initialAngle(), et); - Coord cos_rot_angle, sin_rot_angle; - sincos(_rot_angle, sin_rot_angle, cos_rot_angle); + Coord et = initialAngle().radians() + sweepAngle(); + Linear param(initialAngle().radians(), et); + Coord cosrot, sinrot; + sincos(rotationAngle(), sinrot, cosrot); // order = 4 seems to be enough to get a perfect looking elliptical arc SBasis arc_x = ray(X) * cos(param,4); SBasis arc_y = ray(Y) * sin(param,4); - arc[0] = arc_x * cos_rot_angle - arc_y * sin_rot_angle + Linear(center(X),center(X)); - arc[1] = arc_x * sin_rot_angle + arc_y * cos_rot_angle + Linear(center(Y),center(Y)); + arc[0] = arc_x * cosrot - arc_y * sinrot + Linear(center(X), center(X)); + arc[1] = arc_x * sinrot + arc_y * cosrot + Linear(center(Y), center(Y)); // ensure that endpoints remain exact for ( int d = 0 ; d < 2 ; d++ ) { @@ -887,22 +814,96 @@ D2<SBasis> EllipticalArc::toSBasis() const return arc; } +// All operations that do not contain skew can be evaulated +// without passing through the implicit form of the ellipse, +// which preserves precision. -Curve *EllipticalArc::transformed(Affine const& m) const +void EllipticalArc::operator*=(Translate const &tr) { - Ellipse e(center(X), center(Y), ray(X), ray(Y), _rot_angle); - Ellipse et = e.transformed(m); - Point inner_point = pointAt(0.5); - return et.arc( initialPoint() * m, - inner_point * m, - finalPoint() * m, - isSVGCompliant() ); + _initial_point *= tr; + _final_point *= tr; + _ellipse *= tr; +} + +void EllipticalArc::operator*=(Scale const &s) +{ + _initial_point *= s; + _final_point *= s; + _ellipse *= s; +} + +void EllipticalArc::operator*=(Rotate const &r) +{ + _initial_point *= r; + _final_point *= r; + _ellipse *= r; +} + +void EllipticalArc::operator*=(Zoom const &z) +{ + _initial_point *= z; + _final_point *= z; + _ellipse *= z; +} + +void EllipticalArc::operator*=(Affine const& m) +{ + if (isChord()) { + _initial_point *= m; + _final_point *= m; + _ellipse.setCenter(middle_point(_initial_point, _final_point)); + _ellipse.setRays(0, 0); + _ellipse.setRotationAngle(0); + return; + } + + _initial_point *= m; + _final_point *= m; + _ellipse *= m; + if (m.det() < 0) { + _angles.setSweep(!sweep()); + } + + // ellipse transformation does not preserve its functional form, + // i.e. e.pointAt(0.5)*m and (e*m).pointAt(0.5) can be different. + // We need to recompute start / end angles. + _angles.setInitial(_ellipse.timeAt(_initial_point)); + _angles.setFinal(_ellipse.timeAt(_final_point)); +} + +bool EllipticalArc::operator==(Curve const &c) const +{ + EllipticalArc const *other = dynamic_cast<EllipticalArc const *>(&c); + if (!other) return false; + if (_initial_point != other->_initial_point) return false; + if (_final_point != other->_final_point) return false; + // TODO: all arcs with ellipse rays which are too small + // and fall back to a line should probably be equal + if (rays() != other->rays()) return false; + if (rotationAngle() != other->rotationAngle()) return false; + if (_large_arc != other->_large_arc) return false; + if (sweep() != other->sweep()) return false; + return true; +} + +void EllipticalArc::feed(PathSink &sink, bool moveto_initial) const +{ + if (moveto_initial) { + sink.moveTo(_initial_point); + } + sink.arcTo(ray(X), ray(Y), rotationAngle(), _large_arc, sweep(), _final_point); } -Coord EllipticalArc::map_to_01(Coord angle) const +std::ostream &operator<<(std::ostream &out, EllipticalArc const &ea) { - return map_circular_arc_on_unit_interval(angle, initialAngle(), - finalAngle(), _sweep); + out << "EllipticalArc(" + << ea.initialPoint() << ", " + << format_coord_nice(ea.ray(X)) << ", " << format_coord_nice(ea.ray(Y)) << ", " + << format_coord_nice(ea.rotationAngle()) << ", " + << "large_arc=" << (ea.largeArc() ? "true" : "false") << ", " + << "sweep=" << (ea.sweep() ? "true" : "false") << ", " + << ea.finalPoint() << ")"; + return out; } } // end namespace Geom diff --git a/src/2geom/elliptical-arc.h b/src/2geom/elliptical-arc.h index 5527aa6bc..fbb290dca 100644 --- a/src/2geom/elliptical-arc.h +++ b/src/2geom/elliptical-arc.h @@ -34,90 +34,110 @@ * the specific language governing rights and limitations. */ -#ifndef _2GEOM_ELLIPTICAL_ARC_H_ -#define _2GEOM_ELLIPTICAL_ARC_H_ +#ifndef LIB2GEOM_SEEN_ELLIPTICAL_ARC_H +#define LIB2GEOM_SEEN_ELLIPTICAL_ARC_H #include <algorithm> +#include <2geom/affine.h> #include <2geom/angle.h> #include <2geom/bezier-curve.h> #include <2geom/curve.h> -#include <2geom/affine.h> +#include <2geom/ellipse.h> #include <2geom/sbasis-curve.h> // for non-native methods #include <2geom/utils.h> namespace Geom { -class EllipticalArc : public Curve, public AngleInterval +class EllipticalArc : public Curve { public: - /** @brief Creates an arc with all variables set to zero, and both flags to true. */ + /** @brief Creates an arc with all variables set to zero. */ EllipticalArc() - : AngleInterval(0, 0, true) - , _initial_point(0,0) + : _initial_point(0,0) , _final_point(0,0) - , _rays(0,0) - , _center(0,0) - , _rot_angle(0) - , _large_arc(true) + , _large_arc(false) {} /** @brief Create a new elliptical arc. * @param ip Initial point of the arc - * @param rx First ray of the ellipse - * @param ry Second ray of the ellipse + * @param r Rays of the ellipse as a point * @param rot Angle of rotation of the X axis of the ellipse in radians * @param large If true, the large arc is chosen (always >= 180 degrees), otherwise * the smaller arc is chosen * @param sweep If true, the clockwise arc is chosen, otherwise the counter-clockwise * arc is chosen * @param fp Final point of the arc */ - EllipticalArc( Point ip, Coord rx, Coord ry, + EllipticalArc( Point const &ip, Point const &r, Coord rot_angle, bool large_arc, bool sweep, - Point fp + Point const &fp ) - : AngleInterval(0,0,sweep) - , _initial_point(ip) + : _initial_point(ip) , _final_point(fp) - , _rays(rx, ry) - , _rot_angle(rot_angle) + , _ellipse(0, 0, r[X], r[Y], rot_angle) + , _angles(0, 0, sweep) , _large_arc(large_arc) { - _updateCenterAndAngles(false); + _updateCenterAndAngles(); } - // methods new to EllipticalArc go here + /// Create a new elliptical arc, giving the ellipse's rays as separate coordinates. + EllipticalArc( Point const &ip, Coord rx, Coord ry, + Coord rot_angle, bool large_arc, bool sweep, + Point const &fp + ) + : _initial_point(ip) + , _final_point(fp) + , _ellipse(0, 0, rx, ry, rot_angle) + , _angles(0, 0, sweep) + , _large_arc(large_arc) + { + _updateCenterAndAngles(); + } - /// @name Retrieve and modify parameters + /// @name Retrieve basic information /// @{ - /** @brief Get the interval of angles the arc contains - * @return The interval between the final and initial angles of the arc */ - Interval angleInterval() const { return Interval(initialAngle(), finalAngle()); } + /** @brief Get a coordinate of the elliptical arc's center. * @param d The dimension to retrieve * @return The selected coordinate of the center */ - /** @brief Get the defining ellipse's rotation - * @return Angle between the +X ray of the ellipse and the +X axis */ - Angle rotationAngle() const { - return _rot_angle; - } + Coord center(Dim2 d) const { return _ellipse.center(d); } + + /** @brief Get the arc's center + * @return The arc's center, situated on the intersection of the ellipse's rays */ + Point center() const { return _ellipse.center(); } + /** @brief Get one of the ellipse's rays * @param d Dimension to retrieve * @return The selected ray of the ellipse */ - Coord ray(Dim2 d) const { return _rays[d]; } + Coord ray(Dim2 d) const { return _ellipse.ray(d); } + /** @brief Get both rays as a point * @return Point with X equal to the X ray and Y to Y ray */ - Point rays() const { return _rays; } + Point rays() const { return _ellipse.rays(); } + + /** @brief Get the defining ellipse's rotation + * @return Angle between the +X ray of the ellipse and the +X axis */ + Angle rotationAngle() const { + return _ellipse.rotationAngle(); + } + /** @brief Whether the arc is larger than half an ellipse. * @return True if the arc is larger than \f$\pi\f$, false otherwise */ bool largeArc() const { return _large_arc; } + /** @brief Whether the arc turns clockwise * @return True if the arc makes a clockwise turn when going from initial to final * point, false otherwise */ - bool sweep() const { return _sweep; } - /** @brief Get the line segment connecting the arc's endpoints. - * @return A linear segment with initial and final point correspoding to those of the arc. */ - LineSegment chord() const { return LineSegment(_initial_point, _final_point); } - /** @brief Change the arc's parameters. */ + bool sweep() const { return _angles.sweep(); } + + Angle initialAngle() const { return _angles.initialAngle(); } + Angle finalAngle() const { return _angles.finalAngle(); } + /// @} + + /// @name Modify parameters + /// @{ + + /// Change all of the arc's parameters. void set( Point const &ip, double rx, double ry, double rot_angle, bool large_arc, bool sweep, Point const &fp @@ -125,89 +145,127 @@ public: { _initial_point = ip; _final_point = fp; - _rays[X] = rx; - _rays[Y] = ry; - _rot_angle = Angle(rot_angle); + _ellipse.setRays(rx, ry); + _ellipse.setRotationAngle(rot_angle); + _angles.setSweep(sweep); _large_arc = large_arc; - _sweep = sweep; - _updateCenterAndAngles(isSVGCompliant()); + _updateCenterAndAngles(); } + + /// Change all of the arc's parameters. + void set( Point const &ip, Point const &r, + Angle rot_angle, bool large_arc, bool sweep, + Point const &fp + ) + { + _initial_point = ip; + _final_point = fp; + _ellipse.setRays(r); + _ellipse.setRotationAngle(rot_angle); + _angles.setSweep(sweep); + _large_arc = large_arc; + _updateCenterAndAngles(); + } + /** @brief Change the initial and final point in one operation. * This method exists because modifying any of the endpoints causes rather costly * recalculations of the center and extreme angles. * @param ip New initial point * @param fp New final point */ - void setExtremes(Point const &ip, Point const &fp) { + void setEndpoints(Point const &ip, Point const &fp) { _initial_point = ip; _final_point = fp; - _updateCenterAndAngles(isSVGCompliant()); + _updateCenterAndAngles(); } /// @} - /// @name Access computed parameters of the arc - /// @{ - Coord center(Dim2 d) const { return _center[d]; } - /** @brief Get the arc's center - * @return The arc's center, situated on the intersection of the ellipse's rays */ - Point center() const { return _center; } - /** @brief Get the extent of the arc - * @return The angle between the initial and final point, in arc's angular coordinates */ - Coord sweepAngle() const { - return extent(); - } - /// @} - - /// @name Angular evaluation + /// @name Evaluate the arc as a function /// @{ /** Check whether the arc contains the given angle * @param t The angle to check * @return True if the arc contains the angle, false otherwise */ - bool containsAngle(Coord angle) const; + bool containsAngle(Angle angle) const { return _angles.contains(angle); } + /** @brief Evaluate the arc at the specified angular coordinate * @param t Angle * @return Point corresponding to the given angle */ Point pointAtAngle(Coord t) const; + /** @brief Evaluate one of the arc's coordinates at the specified angle * @param t Angle * @param d The dimension to retrieve * @return Selected coordinate of the arc at the specified angle */ Coord valueAtAngle(Coord t, Dim2 d) const; - /** @brief Retrieve the unit circle transform. + + /// Compute the curve time value corresponding to the given angular value. + Coord timeAtAngle(Angle a) const { return _angles.timeAtAngle(a); } + + /// Compute the angular domain value corresponding to the given time value. + Angle angleAt(Coord t) const { return _angles.angleAt(t); } + + /** @brief Compute the amount by which the angle parameter changes going from start to end. + * This has range \f$(-2\pi, 2\pi)\f$ and thus cannot be represented as instance + * of the class Angle. Add this to the initial angle to obtain the final angle. */ + Coord sweepAngle() const { return _angles.sweepAngle(); } + + /** @brief Get the elliptical angle spanned by the arc. + * This is basically the absolute value of sweepAngle(). */ + Coord angularExtent() const { return _angles.extent(); } + + /// Get the angular interval of the arc. + AngleInterval angularInterval() const { return _angles; } + + /// Evaluate the arc in the curve domain, i.e. \f$[0, 1]\$. + virtual Point pointAt(Coord t) const; + + /// Evaluate a single coordinate on the arc in the curve domain. + virtual Coord valueAt(Coord t, Dim2 d) const; + + /** @brief Compute a transform that maps the unit circle to the arc's ellipse. * Each ellipse can be interpreted as a translated, scaled and rotate unit circle. * This function returns the transform that maps the unit circle to the arc's ellipse. * @return Transform from unit circle to the arc's ellipse */ - Affine unitCircleTransform() const; + Affine unitCircleTransform() const { + Affine result = _ellipse.unitCircleTransform(); + return result; + } + + /** @brief Compute a transform that maps the arc's ellipse to the unit circle. */ + Affine inverseUnitCircleTransform() const { + Affine result = _ellipse.inverseUnitCircleTransform(); + return result; + } /// @} - /** @brief Check whether the arc adheres to SVG 1.1 implementation guidelines */ - virtual bool isSVGCompliant() const { return false; } - - std::pair<EllipticalArc, EllipticalArc> subdivide(Coord t) const { - EllipticalArc* arc1 = static_cast<EllipticalArc*>(portion(0, t)); - EllipticalArc* arc2 = static_cast<EllipticalArc*>(portion(t, 1)); - assert( arc1 != NULL && arc2 != NULL); - std::pair<EllipticalArc, EllipticalArc> arc_pair(*arc1, *arc2); - delete arc1; - delete arc2; - return arc_pair; + /// @name Deal with degenerate ellipses. + /// @{ + /** @brief Check whether both rays are nonzero. + * If they are not, the arc is represented as a line segment instead. */ + bool isChord() const { + return ray(X) == 0 || ray(Y) == 0; } + /** @brief Get the line segment connecting the arc's endpoints. + * @return A linear segment with initial and final point correspoding to those of the arc. */ + LineSegment chord() const { return LineSegment(_initial_point, _final_point); } + /// @} + // implementation of overloads goes here -#ifndef DOXYGEN_SHOULD_SKIP_THIS virtual Point initialPoint() const { return _initial_point; } virtual Point finalPoint() const { return _final_point; } virtual Curve* duplicate() const { return new EllipticalArc(*this); } virtual void setInitial(Point const &p) { _initial_point = p; - _updateCenterAndAngles(isSVGCompliant()); + _updateCenterAndAngles(); } virtual void setFinal(Point const &p) { _final_point = p; - _updateCenterAndAngles(isSVGCompliant()); + _updateCenterAndAngles(); } virtual bool isDegenerate() const { - return ( are_near(ray(X), 0) || are_near(ray(Y), 0) ); + return _initial_point == _final_point; } + virtual bool isLineSegment() const { return isChord(); } virtual Rect boundsFast() const { return boundsExact(); } @@ -218,56 +276,56 @@ public: } virtual std::vector<double> roots(double v, Dim2 d) const; #ifdef HAVE_GSL - virtual std::vector<double> allNearestPoints( Point const& p, double from = 0, double to = 1 ) const; -#endif - virtual double nearestPoint( Point const& p, double from = 0, double to = 1 ) const { + virtual std::vector<double> allNearestTimes( Point const& p, double from = 0, double to = 1 ) const; + virtual double nearestTime( Point const& p, double from = 0, double to = 1 ) const { if ( are_near(ray(X), ray(Y)) && are_near(center(), p) ) { return from; } - return allNearestPoints(p, from, to).front(); + return allNearestTimes(p, from, to).front(); } +#endif + virtual std::vector<CurveIntersection> intersect(Curve const &other, Coord eps=EPSILON) const; virtual int degreesOfFreedom() const { return 7; } virtual Curve *derivative() const; - virtual Curve *transformed(Affine const &m) const; - virtual Curve &operator*=(Translate const &m) { - _initial_point += m.vector(); - _final_point += m.vector(); - _center += m.vector(); - return *this; - } + using Curve::operator*=; + virtual void operator*=(Translate const &tr); + virtual void operator*=(Scale const &s); + virtual void operator*=(Rotate const &r); + virtual void operator*=(Zoom const &z); + virtual void operator*=(Affine const &m); - /** - * The size of the returned vector equals n+1. - */ virtual std::vector<Point> pointAndDerivatives(Coord t, unsigned int n) const; - virtual D2<SBasis> toSBasis() const; - virtual double valueAt(Coord t, Dim2 d) const { - return valueAtAngle(angleAt(t), d); - } - virtual Point pointAt(Coord t) const { - return pointAtAngle(angleAt(t)); - } - virtual Curve* portion(double f, double t) const; - virtual Curve* reverse() const; -#endif + virtual Curve *portion(double f, double t) const; + virtual Curve *reverse() const; + virtual bool operator==(Curve const &c) const; + virtual void feed(PathSink &sink, bool moveto_initial) const; -protected: - void _updateCenterAndAngles(bool svg); +private: + void _updateCenterAndAngles(); + void _filterIntersections(std::vector<ShapeIntersection> &xs, bool is_first) const; Point _initial_point, _final_point; - Point _rays, _center; - Angle _rot_angle; + Ellipse _ellipse; + AngleInterval _angles; bool _large_arc; - -private: - Coord map_to_01(Coord angle) const; }; // end class EllipticalArc + +// implemented in elliptical-arc-from-sbasis.cpp +/** @brief Fit an elliptical arc to an SBasis fragment. + * @relates EllipticalArc */ +bool arc_from_sbasis(EllipticalArc &ea, D2<SBasis> const &in, + double tolerance = EPSILON, unsigned num_samples = 20); + +/** @brief Debug output for elliptical arcs. + * @relates EllipticalArc */ +std::ostream &operator<<(std::ostream &out, EllipticalArc const &ea); + } // end namespace Geom -#endif // _2GEOM_ELLIPTICAL_ARC_H_ +#endif // LIB2GEOM_SEEN_ELLIPTICAL_ARC_H /* Local Variables: diff --git a/src/2geom/exception.h b/src/2geom/exception.h index 12736d639..ab6f82142 100644 --- a/src/2geom/exception.h +++ b/src/2geom/exception.h @@ -38,8 +38,8 @@ * */ -#ifndef LIB2GEOM_EXCEPTION_HEADER -#define LIB2GEOM_EXCEPTION_HEADER +#ifndef LIB2GEOM_SEEN_EXCEPTION_H +#define LIB2GEOM_SEEN_EXCEPTION_H #include <exception> #include <sstream> diff --git a/src/2geom/forward.h b/src/2geom/forward.h index 70cac1f7d..27909242c 100644 --- a/src/2geom/forward.h +++ b/src/2geom/forward.h @@ -32,14 +32,12 @@ * the specific language governing rights and limitations. */ -#ifndef SEEN_GEOM_FORWARD_H -#define SEEN_GEOM_FORWARD_H - -#include <vector> // include this dependency so PathVector can be defined more explicitly +#ifndef LIB2GEOM_SEEN_FORWARD_H +#define LIB2GEOM_SEEN_FORWARD_H namespace Geom { -// basic types +// primitives typedef double Coord; typedef int IntCoord; class Point; @@ -63,23 +61,29 @@ typedef GenericOptRect<IntCoord> OptIntRect; class Linear; class Bezier; class SBasis; +class Poly; + +// shapes +class Circle; +class Ellipse; +class ConvexHull; // curves class Curve; class SBasisCurve; class BezierCurve; -class HLineSegment; -class VLineSegment; template <unsigned degree> class BezierCurveN; typedef BezierCurveN<1> LineSegment; typedef BezierCurveN<2> QuadraticBezier; typedef BezierCurveN<3> CubicBezier; class EllipticalArc; -class SVGEllipticalArc; // paths and path sequences class Path; -typedef std::vector<Path> PathVector; +class PathVector; +struct PathTime; +class PathInterval; +struct PathVectorTime; // errors class Exception; @@ -103,11 +107,6 @@ class Zoom; template <typename> class D2; template <typename> class Piecewise; -class Shape; -class Region; -class Hat; -class Tri; - // misc class SVGPathSink; template <typename> class SVGPathGenerator; diff --git a/src/2geom/generic-interval.h b/src/2geom/generic-interval.h index f6d4718de..716390e57 100644 --- a/src/2geom/generic-interval.h +++ b/src/2geom/generic-interval.h @@ -90,13 +90,24 @@ public: } /// @} - /// @name Inspect endpoints. + /// @name Inspect contained values. /// @{ C min() const { return _b[0]; } C max() const { return _b[1]; } C extent() const { return max() - min(); } C middle() const { return (max() + min()) / 2; } bool isSingular() const { return min() == max(); } + C operator[](unsigned i) const { assert(i < 2); return _b[i]; } + C clamp(C val) const { + if (val < min()) return min(); + if (val > max()) return max(); + return val; + } + /// Return the closer end of the interval. + C nearestEnd(C val) const { + C dmin = std::abs(val - min()), dmax = std::abs(val - max()); + return dmin <= dmax ? min() : max(); + } /// @} /// @name Test coordinates and other intervals for inclusion. @@ -138,6 +149,16 @@ public: _b[1] = val; } } + /// Set both ends of the interval simultaneously + void setEnds(C a, C b) { + if (a <= b) { + _b[0] = a; + _b[1] = b; + } else { + _b[0] = b; + _b[1] = a; + } + } /** @brief Extend the interval to include the given number. */ void expandTo(C val) { if(val < _b[0]) _b[0] = val; @@ -271,6 +292,8 @@ public: /** @brief Check whether this interval is empty. */ bool isEmpty() { return !*this; }; + /// Alias of isEmpty() for STL similarity. + bool empty() { return !*this; } /** @brief Union with another interval, gracefully handling empty ones. */ void unionWith(GenericOptInterval<C> const &a) { @@ -317,14 +340,14 @@ inline GenericOptInterval<C> operator&(GenericInterval<C> const &a, GenericInter return GenericOptInterval<C>(a) & GenericOptInterval<C>(b); } -//#ifdef _GLIBCXX_IOSTREAM +#ifdef _GLIBCXX_IOSTREAM template <typename C> inline std::ostream &operator<< (std::ostream &os, Geom::GenericInterval<C> const &I) { os << "Interval("<<I.min() << ", "<<I.max() << ")"; return os; } -//#endif +#endif } // namespace Geom #endif // !LIB2GEOM_SEEN_GENERIC_INTERVAL_H diff --git a/src/2geom/generic-rect.h b/src/2geom/generic-rect.h index 9ad0e60b0..f7d722757 100644 --- a/src/2geom/generic-rect.h +++ b/src/2geom/generic-rect.h @@ -64,6 +64,10 @@ class GenericRect protected: CInterval f[2]; public: + typedef CInterval D1Value; + typedef CInterval &D1Reference; + typedef CInterval const &D1ConstReference; + /// @name Create rectangles. /// @{ /** @brief Create a rectangle that contains only the point at (0,0). */ @@ -180,12 +184,34 @@ public: /** @brief Compute rectangle's area. */ C area() const { return f[X].extent() * f[Y].extent(); } /** @brief Check whether the rectangle has zero area. */ - bool hasZeroArea() const { return (area() == 0); } + bool hasZeroArea() const { return f[X].isSingular() || f[Y].isSingular(); } /** @brief Get the larger extent (width or height) of the rectangle. */ C maxExtent() const { return std::max(f[X].extent(), f[Y].extent()); } /** @brief Get the smaller extent (width or height) of the rectangle. */ C minExtent() const { return std::min(f[X].extent(), f[Y].extent()); } + + /** @brief Clamp point to the rectangle. */ + CPoint clamp(CPoint const &p) const { + CPoint result(f[X].clamp(p[X]), f[Y].clamp(p[Y])); + return result; + } + /** @brief Get the nearest point on the edge of the rectangle. */ + CPoint nearestEdgePoint(CPoint const &p) const { + CPoint result = p; + if (!contains(p)) { + result = clamp(p); + } else { + C cx = f[X].nearestEnd(p[X]); + C cy = f[Y].nearestEnd(p[Y]); + if (std::abs(cx - p[X]) <= std::abs(cy - p[Y])) { + result[X] = cx; + } else { + result[Y] = cy; + } + } + return result; + } /// @} /// @name Test other rectangles and points for inclusion. @@ -328,6 +354,10 @@ class GenericOptRect typedef typename CoordTraits<C>::OptRectType OptCRect; typedef boost::optional<CRect> Base; public: + typedef CInterval D1Value; + typedef CInterval &D1Reference; + typedef CInterval const &D1ConstReference; + /// @name Create potentially empty rectangles. /// @{ GenericOptRect() : Base() {} @@ -483,13 +513,13 @@ inline bool GenericRect<C>::contains(OptCRect const &r) const { return !r || contains(*r); } -//#ifdef _GLIBCXX_IOSTREAM +#ifdef _GLIBCXX_IOSTREAM template <typename C> inline std::ostream &operator<<(std::ostream &out, GenericRect<C> const &r) { out << "X: " << r[X] << " Y: " << r[Y]; return out; } -//#endif +#endif } // end namespace Geom diff --git a/src/2geom/geom.cpp b/src/2geom/geom.cpp index d3cf0ca73..bf9b0a721 100644 --- a/src/2geom/geom.cpp +++ b/src/2geom/geom.cpp @@ -10,8 +10,17 @@ #include <algorithm> #include <2geom/rect.h> +using std::swap; + namespace Geom { +enum IntersectorKind { + intersects = 0, + parallel, + coincident, + no_intersection +}; + /** * Finds the intersection of the two (infinite) lines * defined by the points p such that dot(n0, p) == d0 and dot(n1, p) == d1. @@ -305,7 +314,7 @@ rect_line_intersect(Geom::Point const &c0, Geom::Point const &c1, Point dir1 (results[1] - results[0]); Point dir2 (p1 - p0); if (dot(dir1, dir2) < 0) { - std::swap(results[0], results[1]); + swap(results[0], results[1]); } } @@ -363,7 +372,7 @@ int centroid(std::vector<Geom::Point> const &p, Geom::Point& centroid, double &a Geom::Point centroid_tmp(0,0); double atmp = 0; for (unsigned i = n-1, j = 0; j < n; i = j, j++) { - const double ai = -cross(p[j], p[i]); + const double ai = cross(p[j], p[i]); atmp += ai; centroid_tmp += (p[j] + p[i])*ai; // first moment. } diff --git a/src/2geom/geom.h b/src/2geom/geom.h index 5aeded23d..6ba812254 100644 --- a/src/2geom/geom.h +++ b/src/2geom/geom.h @@ -44,52 +44,10 @@ namespace Geom { -enum IntersectorKind { - intersects = 0, - parallel, - coincident, - no_intersection -}; - -int -intersector_ccw(const Geom::Point& p0, const Geom::Point& p1, - const Geom::Point& p2); - -/* intersectors */ - -#if 0 -// Use the new routines provided in line.h - -IntersectorKind -line_intersection(Geom::Point const &n0, double const d0, - Geom::Point const &n1, double const d1, - Geom::Point &result); - -IntersectorKind -segment_intersect(Geom::Point const &p00, Geom::Point const &p01, - Geom::Point const &p10, Geom::Point const &p11, - Geom::Point &result); - -IntersectorKind -line_twopoint_intersect(Geom::Point const &p00, Geom::Point const &p01, - Geom::Point const &p10, Geom::Point const &p11, - Geom::Point &result); -#endif - -#if 0 -std::vector<Geom::Point> -rect_line_intersect(Geom::Point const &E, Geom::Point const &F, - Geom::Point const &p0, Geom::Point const &p1); -#endif - boost::optional<Geom::LineSegment> rect_line_intersect(Geom::Rect &r, Geom::LineSegment ls); -boost::optional<Geom::LineSegment> -rect_line_intersect(Geom::Rect &r, - Geom::Line l); - int centroid(std::vector<Geom::Point> const &p, Geom::Point& centroid, double &area); } diff --git a/src/2geom/hvlinesegment.h b/src/2geom/hvlinesegment.h deleted file mode 100644 index d2b9d6310..000000000 --- a/src/2geom/hvlinesegment.h +++ /dev/null @@ -1,283 +0,0 @@ -/** - * \file - * \brief Horizontal and vertical line segment - *//* - * Authors: - * Marco Cecchetti <mrcekets at gmail.com> - * Krzysztof Kosiński <tweenk.pl@gmail.com> - * Copyright 2008-2011 Authors - * - * This library is free software; you can redistribute it and/or - * modify it either under the terms of the GNU Lesser General Public - * License version 2.1 as published by the Free Software Foundation - * (the "LGPL") or, at your option, under the terms of the Mozilla - * Public License Version 1.1 (the "MPL"). If you do not alter this - * notice, a recipient may use your version of this file under either - * the MPL or the LGPL. - * - * You should have received a copy of the LGPL along with this library - * in the file COPYING-LGPL-2.1; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * You should have received a copy of the MPL along with this library - * in the file COPYING-MPL-1.1 - * - * The contents of this file are subject to the Mozilla Public License - * Version 1.1 (the "License"); you may not use this file except in - * compliance with the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY - * OF ANY KIND, either express or implied. See the LGPL or the MPL for - * the specific language governing rights and limitations. - */ - -#ifndef LIB2GEOM_SEEN_HVLINESEGMENT_H -#define LIB2GEOM_SEEN_HVLINESEGMENT_H - -#include <2geom/bezier-curve.h> - -namespace Geom -{ - -template <Dim2 axis> -class AxisLineSegment : public LineSegment -{ -public: - static const Dim2 other_axis = static_cast<Dim2>((axis + 1) % 2); -#ifndef DOXYGEN_SHOULD_SKIP_THIS - virtual void setInitial(Point const &p) { - Point f = finalPoint(); - f[axis] = p[axis]; - LineSegment::setInitial(p); - LineSegment::setFinal(f); - } - virtual void setFinal(Point const &p) { - Point i = initialPoint(); - i[axis] = p[axis]; - LineSegment::setFinal(p); - LineSegment::setInitial(i); - } - virtual Rect boundsFast() const { return boundsExact(); } - virtual Rect boundsExact() const { Rect r(initialPoint(), finalPoint()); return r; } - virtual int degreesOfFreedom() const { return 3; } - virtual std::vector<Coord> roots(Coord v, Dim2 d) const { - std::vector<Coord> result; - if (d == axis) { - if ( v >= initialPoint()[axis] && v <= finalPoint()[axis] ) { - Coord t = 0; - if (!isDegenerate()) - t = (v - initialPoint()[axis]) / (finalPoint()[axis] - initialPoint()[axis]); - result.push_back(t); - } - } else { - if (v == initialPoint()[other_axis]) { - if (!isDegenerate()) - THROW_INFINITESOLUTIONS(0); - result.push_back(0); - } - } - return result; - } - virtual Coord nearestPoint( Point const &p, Coord from = 0, Coord to = 1 ) const { - if ( from > to ) std::swap(from, to); - Coord xfrom = valueAt(from, axis); - Coord xto = valueAt(to, axis); - if ( xfrom > xto ) { - std::swap(xfrom, xto); - std::swap(from, to); - } - if ( p[axis] > xfrom && p[axis] < xto ) { - return (p[axis] - initialPoint()[axis]) / (finalPoint()[axis] - initialPoint()[axis]); - } - else if ( p[X] <= xfrom ) - return from; - else - return to; - } - virtual Point pointAt(Coord t) const { - if ( t < 0 || t > 1 ) - THROW_RANGEERROR("AxisLineSegment: Time value out of range"); - Point ret = initialPoint() + t * (finalPoint() - initialPoint()); - return ret; - } - virtual Coord valueAt(Coord t, Dim2 d) const { - if ( t < 0 || t > 1 ) - THROW_RANGEERROR("AxisLineSegment: Time value out of range"); - if (d != axis) return initialPoint()[other_axis]; - return initialPoint()[axis] + t * (finalPoint()[axis] - initialPoint()[axis]); - } - virtual std::vector<Point> pointAndDerivatives(Coord t, unsigned n) const { - std::vector<Point> result; - result.push_back(pointAt(t)); - if (n > 0) { - Point der = finalPoint() - initialPoint(); - result.push_back( der ); - } - if (n > 1) { - /* higher order derivatives are zero, - * so the other n-1 vector elements are (0,0) */ - result.insert( result.end(), n-1, Point(0, 0) ); - } - return result; - } -#endif -protected: - AxisLineSegment(Point const &p0, Point const &p1) : LineSegment(p0, p1) {} - AxisLineSegment() {} -}; - -class HLineSegment : public AxisLineSegment<X> -{ -public: - HLineSegment() {} - HLineSegment(Coord x0, Coord x1, Coord y) - : AxisLineSegment<X>(Point(x0, y), Point(x1, y)) - {} - - HLineSegment(Point const& p, Coord len) - : AxisLineSegment<X>(p, Point(p[X] + len, p[Y])) - {} - - HLineSegment(Point const& p0, Point const& p1) - : AxisLineSegment<X>(p0, p1) - { - if ( p0[Y] != p1[Y] ) { - THROW_RANGEERROR("HLineSegment::HLineSegment passed points should " - "have the same Y value"); - } - } - - Coord getY() { return initialPoint()[Y]; } - void setInitialX(Coord x) { - LineSegment::setInitial(Point(x, initialPoint()[Y])); - } - void setFinalX(Coord x) { - LineSegment::setFinal(Point(x, initialPoint()[Y])); - } - void setY(Coord y) { - LineSegment::setInitial( Point(initialPoint()[X], y) ); - LineSegment::setFinal( Point(finalPoint()[X], y) ); - } - std::pair<HLineSegment, HLineSegment> subdivide(Coord t) const { - std::pair<HLineSegment, HLineSegment> result; - Point p = pointAt(t); - result.first.setInitial(initialPoint()); - result.first.setFinal(p); - result.second.setInitial(p); - result.second.setFinal(finalPoint()); - return result; - } - -#ifndef DOXYGEN_SHOULD_SKIP_THIS - virtual Curve* duplicate() const { return new HLineSegment(*this); } - virtual Curve *portion(Coord f, Coord t) const { - Point ip = pointAt(f); - Point ep = pointAt(t); - return new HLineSegment(ip[X], ep[X], ip[Y]); - } - virtual Curve *reverse() const { - Point ip = initialPoint(); - return new HLineSegment(finalPoint()[X], ip[X], ip[Y]); - } - virtual Curve *transformed(Affine const & m) const { - Point ip = initialPoint() * m; - Point ep = finalPoint() * m; - // cannot afford to lose precision here since it can lead to discontinuous paths - if (m.isZoom(0)) { - return new HLineSegment(ip[X], ep[X], ip[Y]); - } else { - return new LineSegment(ip, ep); - } - } - virtual Curve *derivative() const { - Coord x = finalPoint()[X] - initialPoint()[X]; - return new HLineSegment(x, x, 0); - } -#endif -}; // end class HLineSegment - - -class VLineSegment : public AxisLineSegment<Y> -{ -public: - VLineSegment() {} - - VLineSegment(Coord x, Coord y0, Coord y1) - : AxisLineSegment<Y>(Point(x, y0), Point(x, y1)) - {} - - VLineSegment(Point const& _p, Coord _length) - : AxisLineSegment<Y>(_p, Point(_p[X], _p[Y] + _length)) - {} - - VLineSegment(Point const& _p0, Point const& _p1) - : AxisLineSegment<Y>(_p0, _p1) - { - if ( _p0[X] != _p1[X] ) { - THROW_RANGEERROR("VLineSegment::VLineSegment passed points should " - "have the same X value"); - } - } - - Coord getX() { return initialPoint()[X]; } - void setInitialY(Coord _y) { - LineSegment::setInitial( Point(initialPoint()[X], _y) ); - } - void setFinalY(Coord _y) { - LineSegment::setFinal( Point(finalPoint()[Y], _y) ); - } - void setX(Coord _x) { - LineSegment::setInitial( Point(_x, initialPoint()[Y]) ); - LineSegment::setFinal( Point(_x, finalPoint()[Y]) ); - } - std::pair<VLineSegment, VLineSegment> subdivide(Coord t) const { - std::pair<VLineSegment, VLineSegment> result; - Point p = pointAt(t); - result.first.setInitial(initialPoint()); - result.first.setFinal(p); - result.second.setInitial(p); - result.second.setFinal(finalPoint()); - return result; - } - -#ifndef DOXYGEN_SHOULD_SKIP_THIS - virtual Curve *duplicate() const { return new VLineSegment(*this); } - virtual Curve *portion(Coord f, Coord t) const { - Point ip = pointAt(f); - Coord epy = valueAt(t, Y); - return new VLineSegment(ip[X], ip[Y], epy); - } - virtual Curve *reverse() const { - Point ip = initialPoint(); - return new VLineSegment(ip[X], finalPoint()[Y], ip[Y]); - } - virtual Curve *transformed(Affine const & m) const { - Point ip = initialPoint() * m; - Point ep = finalPoint() * m; - if (m.isZoom()) { - return new VLineSegment(ip[X], ip[Y], ep[Y]); - } else { - return new LineSegment(ip, ep); - } - } - virtual Curve* derivative() const { - Coord y = finalPoint()[Y] - initialPoint()[Y]; - return new VLineSegment(0, y, y); - } -#endif -}; // end class VLineSegment - -} // end namespace Geom - -#endif // LIB2GEOM_SEEN_HVLINESEGMENT_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/2geom/int-point.h b/src/2geom/int-point.h index 1a16ecb7a..7c737b1d7 100644 --- a/src/2geom/int-point.h +++ b/src/2geom/int-point.h @@ -120,30 +120,53 @@ public: } /// @} - /** @brief Lexicographical ordering functor. */ - template <Dim2 d> struct LexOrder; + /** @brief Lexicographical ordering functor. + * @param d The more significant dimension */ + template <Dim2 d> struct LexLess; + /** @brief Lexicographical ordering functor. + * @param d The more significant dimension */ + template <Dim2 d> struct LexGreater; /** @brief Lexicographical ordering functor with runtime dimension. */ - class LexOrderRt { - public: - LexOrderRt(Dim2 d) : dim(d) {} - inline bool operator()(IntPoint const &a, IntPoint const &b); + struct LexLessRt { + LexLessRt(Dim2 d) : dim(d) {} + inline bool operator()(IntPoint const &a, IntPoint const &b) const; + private: + Dim2 dim; + }; + /** @brief Lexicographical ordering functor with runtime dimension. */ + struct LexGreaterRt { + LexGreaterRt(Dim2 d) : dim(d) {} + inline bool operator()(IntPoint const &a, IntPoint const &b) const; private: Dim2 dim; }; }; -template<> struct IntPoint::LexOrder<X> { - bool operator()(IntPoint const &a, IntPoint const &b) { +template<> struct IntPoint::LexLess<X> { + bool operator()(IntPoint const &a, IntPoint const &b) const { return a[X] < b[X] || (a[X] == b[X] && a[Y] < b[Y]); } }; -template<> struct IntPoint::LexOrder<Y> { - bool operator()(IntPoint const &a, IntPoint const &b) { +template<> struct IntPoint::LexLess<Y> { + bool operator()(IntPoint const &a, IntPoint const &b) const { return a[Y] < b[Y] || (a[Y] == b[Y] && a[X] < b[X]); } }; -inline bool IntPoint::LexOrderRt::operator()(IntPoint const &a, IntPoint const &b) { - return dim ? IntPoint::LexOrder<Y>()(a, b) : IntPoint::LexOrder<X>()(a, b); +template<> struct IntPoint::LexGreater<X> { + bool operator()(IntPoint const &a, IntPoint const &b) const { + return a[X] > b[X] || (a[X] == b[X] && a[Y] > b[Y]); + } +}; +template<> struct IntPoint::LexGreater<Y> { + bool operator()(IntPoint const &a, IntPoint const &b) const { + return a[Y] > b[Y] || (a[Y] == b[Y] && a[X] > b[X]); + } +}; +inline bool IntPoint::LexLessRt::operator()(IntPoint const &a, IntPoint const &b) const { + return dim ? IntPoint::LexLess<Y>()(a, b) : IntPoint::LexLess<X>()(a, b); +} +inline bool IntPoint::LexGreaterRt::operator()(IntPoint const &a, IntPoint const &b) const { + return dim ? IntPoint::LexGreater<Y>()(a, b) : IntPoint::LexGreater<X>()(a, b); } } // namespace Geom diff --git a/src/2geom/intersection-graph.cpp b/src/2geom/intersection-graph.cpp new file mode 100644 index 000000000..e18561f67 --- /dev/null +++ b/src/2geom/intersection-graph.cpp @@ -0,0 +1,394 @@ +/** + * \file + * \brief Intersection graph for Boolean operations + *//* + * Authors: + * Krzysztof Kosiński <tweenk.pl@gmail.com> + * + * Copyright 2015 Authors + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + */ + +#include <2geom/intersection-graph.h> +#include <2geom/path.h> +#include <2geom/pathvector.h> +#include <iostream> +#include <iterator> + +namespace Geom { + +struct PathIntersectionGraph::IntersectionVertexLess { + bool operator()(IntersectionVertex const &a, IntersectionVertex const &b) const { + return a.pos < b.pos; + } +}; + +/** @class PathIntersectionGraph + * @brief Intermediate data for computing Boolean operations on paths. + * + * This class implements the Greiner-Hormann clipping algorithm, + * with improvements inspired by Foster and Overfelt as well as some + * original contributions. + * + * @ingroup Paths + */ + +PathIntersectionGraph::PathIntersectionGraph(PathVector const &a, PathVector const &b, Coord precision) + : _a(a) + , _b(b) +{ + if (a.empty() || b.empty()) return; + + // all paths must be closed, otherwise we will miss some intersections + for (std::size_t i = 0; i < a.size(); ++i) { + _a[i].close(); + } + for (std::size_t i = 0; i < b.size(); ++i) { + _b[i].close(); + } + + std::vector<PVIntersection> pxs = _a.intersect(_b, precision); + // NOTE: this early return means that the path data structures will not be created + // if there are no intersections at all! + if (pxs.empty()) return; + + // prepare intersection lists for each path component + for (std::size_t i = 0; i < _a.size(); ++i) { + _apaths.push_back(new PathData()); + } + for (std::size_t i = 0; i < _b.size(); ++i) { + _bpaths.push_back(new PathData()); + } + + for (std::size_t i = 0; i < pxs.size(); ++i) { + IntersectionVertex *xa, *xb; + xa = new IntersectionVertex(); + xb = new IntersectionVertex(); + xa->processed = xb->processed = false; + xa->pos = pxs[i].first; + xb->pos = pxs[i].second; + xa->p = xb->p = pxs[i].point(); + xa->neighbor = xb; + xb->neighbor = xa; + _xs.push_back(xa); + _xs.push_back(xb); + _apaths[xa->pos.path_index].xlist.push_back(*xa); + _bpaths[xb->pos.path_index].xlist.push_back(*xb); + } + + for (std::size_t i = 0; i < _apaths.size(); ++i) { + _apaths[i].xlist.sort(IntersectionVertexLess()); + } + for (std::size_t i = 0; i < _bpaths.size(); ++i) { + _bpaths[i].xlist.sort(IntersectionVertexLess()); + } + + typedef IntersectionList::iterator Iter; + + // determine in/out/on flags using winding + for (unsigned npv = 0; npv < 2; ++npv) { + boost::ptr_vector<PathData> &ls = npv ? _bpaths : _apaths; + boost::ptr_vector<PathData> &ols = npv ? _apaths : _bpaths; + PathVector const &pv = npv ? b : a; + PathVector const &other = npv ? a : b; + + for (unsigned li = 0; li < ls.size(); ++li) { + IntersectionList &xl = ls[li].xlist; + for (Iter i = xl.begin(); i != xl.end(); ++i) { + Iter n = boost::next(i); + if (n == xl.end()) { + n = xl.begin(); + } + std::size_t pi = i->pos.path_index; + + PathInterval ival = forward_interval(i->pos, n->pos, pv[pi].size()); + PathTime mid = ival.inside(precision); + + // TODO check for degenerate cases + int w = other.winding(pv[pi].pointAt(mid)); + if (w % 2) { + i->next = POINT_INSIDE; + n->previous = POINT_INSIDE; + } else { + i->next = POINT_OUTSIDE; + n->previous = POINT_OUTSIDE; + } + } + + // remove intersections that don't change between in/out + // and assign exit / entry flags + for (Iter i = xl.begin(); i != xl.end();) { + if (i->previous == i->next) { + IntersectionList &oxl = ols[i->neighbor->pos.path_index].xlist; + oxl.erase(oxl.iterator_to(*i->neighbor)); + xl.erase(i++); + if (i->next == POINT_INSIDE) { + ++ls[li].removed_in; + } else { + ++ls[li].removed_out; + } + } else { + i->entry = ((i->next == POINT_INSIDE) && (i->previous == POINT_OUTSIDE)); + ++i; + } + } + } + } +} + +PathVector PathIntersectionGraph::getUnion() +{ + PathVector result = _getResult(false, false); + _handleNonintersectingPaths(result, 0, false); + _handleNonintersectingPaths(result, 1, false); + return result; +} + +PathVector PathIntersectionGraph::getIntersection() +{ + PathVector result = _getResult(true, true); + _handleNonintersectingPaths(result, 0, true); + _handleNonintersectingPaths(result, 1, true); + return result; +} + +PathVector PathIntersectionGraph::getAminusB() +{ + PathVector result = _getResult(false, true); + _handleNonintersectingPaths(result, 0, false); + _handleNonintersectingPaths(result, 1, true); + return result; +} + +PathVector PathIntersectionGraph::getBminusA() +{ + PathVector result = _getResult(true, false); + _handleNonintersectingPaths(result, 1, false); + _handleNonintersectingPaths(result, 0, true); + return result; +} + +PathVector PathIntersectionGraph::getXOR() +{ + PathVector r1 = getAminusB(); + PathVector r2 = getBminusA(); + std::copy(r2.begin(), r2.end(), std::back_inserter(r1)); + return r1; +} + +std::size_t PathIntersectionGraph::size() const +{ + std::size_t result = 0; + for (std::size_t i = 0; i < _apaths.size(); ++i) { + result += _apaths[i].xlist.size(); + } + return result; +} + +std::vector<Point> PathIntersectionGraph::intersectionPoints() const +{ + std::vector<Point> result; + + typedef IntersectionList::const_iterator Iter; + for (std::size_t i = 0; i < _apaths.size(); ++i) { + for (Iter j = _apaths[i].xlist.begin(); j != _apaths[i].xlist.end(); ++j) { + result.push_back(j->p); + } + } + return result; +} + +PathVector PathIntersectionGraph::_getResult(bool enter_a, bool enter_b) +{ + typedef IntersectionList::iterator Iter; + PathVector result; + if (_xs.empty()) return result; + + // reset processed status + for (unsigned npv = 0; npv < 2; ++npv) { + boost::ptr_vector<PathData> &ls = npv ? _bpaths : _apaths; + for (std::size_t li = 0; li < ls.size(); ++li) { + for (Iter k = ls[li].xlist.begin(); k != ls[li].xlist.end(); ++k) { + k->processed = false; + } + } + } + + unsigned n_processed = 0; + + while (true) { + PathVector const *cur = &_a, *other = &_b; + boost::ptr_vector<PathData> *lscur = &_apaths, *lsother = &_bpaths; + + // find unprocessed intersection + Iter i; + if (!_findUnprocessed(i)) break; + + result.push_back(Path(i->p)); + result.back().setStitching(true); + + while (!i->processed) { + Iter prev = i; + std::size_t pi = i->pos.path_index; + // determine which direction to go + // union: always go outside + // intersection: always go inside + // a minus b: go inside in b, outside in a + // b minus a: go inside in a, outside in b + bool reverse = false; + if (cur == &_a) { + reverse = i->entry ^ enter_a; + } else { + reverse = i->entry ^ enter_b; + } + + // get next intersection + if (reverse) { + if (i == (*lscur)[pi].xlist.begin()) { + i = (*lscur)[pi].xlist.end(); + } + --i; + } else { + ++i; + if (i == (*lscur)[pi].xlist.end()) { + i = (*lscur)[pi].xlist.begin(); + } + } + + // append portion of path + PathInterval ival = PathInterval::from_direction( + prev->pos.asPathTime(), i->pos.asPathTime(), + reverse, (*cur)[pi].size()); + + (*cur)[pi].appendPortionTo(result.back(), ival, prev->p, i->p); + + // mark both vertices as processed + prev->processed = true; + i->processed = true; + n_processed += 2; + + // switch to the other path + i = (*lsother)[i->neighbor->pos.path_index].xlist.iterator_to(*i->neighbor); + std::swap(lscur, lsother); + std::swap(cur, other); + } + result.back().close(true); + + assert(!result.back().empty()); + } + + assert(n_processed == size() * 2); + + return result; +} + +void PathIntersectionGraph::_handleNonintersectingPaths(PathVector &result, int which, bool inside) +{ + /* Every component that has any intersections will be processed by _getResult. + * Here we take care of paths that don't have any intersections. They are either + * completely inside or completely outside the other pathvector. We test this by + * evaluating the winding rule at the initial point. If inside is true and + * the path is inside, we add it to the result. + */ + boost::ptr_vector<PathData> const &ls = which ? _bpaths : _apaths; + PathVector const &cur = which ? _b : _a; + PathVector const &other = which ? _a : _b; + + for (std::size_t i = 0; i < cur.size(); ++i) { + // the path data vector might have been left empty if there were no intersections at all + bool has_path_data = !ls.empty(); + // Skip if the path has intersections + if (has_path_data && !ls[i].xlist.empty()) continue; + bool path_inside = false; + + // If the path had any intersections removed, use the result of that, + // since one of those might have been at the initial point. + // Also, it saves time. + if (has_path_data && ls[i].removed_in != 0) { + path_inside = true; + } else if (has_path_data && ls[i].removed_out != 0) { + path_inside = false; + } else { + int w = other.winding(cur[i].initialPoint()); + path_inside = w % 2 != 0; + } + + if (path_inside == inside) { + result.push_back(cur[i]); + } + } +} + +bool PathIntersectionGraph::_findUnprocessed(IntersectionList::iterator &result) +{ + typedef IntersectionList::iterator Iter; + + Iter it, last; + + for (std::size_t k = 0; k < _apaths.size(); ++k) { + it = _apaths[k].xlist.begin(); + last = _apaths[k].xlist.end(); + for (; it != last; ++it) { + if (!it->_hook.is_linked()) { + // this intersection was removed since it did not change inside/outside status + continue; + } + if (!it->processed) { + result = it; + return true; + } + } + } + + return false; +} + + +std::ostream &operator<<(std::ostream &os, PathIntersectionGraph const &pig) +{ + os << "Intersection graph:\n" + << pig._xs.size()/2 << " total intersections\n" + << pig.size() << " considered intersections\n"; + for (std::size_t i = 0; i < pig._apaths.size(); ++i) { + typedef PathIntersectionGraph::IntersectionList::const_iterator Iter; + for (Iter j = pig._apaths[i].xlist.begin(); j != pig._apaths[i].xlist.end(); ++j) { + os << j->pos << " - " << j->neighbor->pos << " @ " << j->p << "\n"; + } + } + return os; +} + +} // namespace Geom + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/2geom/intersection-graph.h b/src/2geom/intersection-graph.h new file mode 100644 index 000000000..bd9aaee81 --- /dev/null +++ b/src/2geom/intersection-graph.h @@ -0,0 +1,128 @@ +/** + * \file + * \brief Path intersection graph + *//* + * Authors: + * Krzysztof Kosiński <tweenk.pl@gmail.com> + * + * Copyright 2015 Authors + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + */ + +#ifndef SEEN_LIB2GEOM_INTERSECTION_GRAPH_H +#define SEEN_LIB2GEOM_INTERSECTION_GRAPH_H + +#include <set> +#include <vector> +#include <boost/ptr_container/ptr_vector.hpp> +#include <boost/intrusive/list.hpp> +#include <2geom/forward.h> +#include <2geom/pathvector.h> + +namespace Geom { + +class PathIntersectionGraph +{ + // this is called PathIntersectionGraph so that we can also have a class for polygons, + // e.g. PolygonIntersectionGraph, which is going to be significantly faster +public: + PathIntersectionGraph(PathVector const &a, PathVector const &b, Coord precision = EPSILON); + + PathVector getUnion(); + PathVector getIntersection(); + PathVector getAminusB(); + PathVector getBminusA(); + PathVector getXOR(); + + /// Returns the number of intersections used when computing Boolean operations. + std::size_t size() const; + std::vector<Point> intersectionPoints() const; + +private: + enum InOutFlag { + POINT_INSIDE, + POINT_OUTSIDE + }; + + struct IntersectionVertex { + boost::intrusive::list_member_hook<> _hook; + PathVectorTime pos; + Point p; // guarantees that endpoints are exact + IntersectionVertex *neighbor; + bool entry; // going in +t direction enters the other path + InOutFlag previous; + InOutFlag next; + bool processed; // TODO: use intrusive unprocessed list instead + }; + + typedef boost::intrusive::list + < IntersectionVertex + , boost::intrusive::member_hook + < IntersectionVertex + , boost::intrusive::list_member_hook<> + , &IntersectionVertex::_hook + > + > IntersectionList; + + struct PathData { + IntersectionList xlist; + unsigned removed_in; + unsigned removed_out; + + PathData() + : removed_in(0) + , removed_out(0) + {} + }; + + struct IntersectionVertexLess; + + PathVector _getResult(bool enter_a, bool enter_b); + void _handleNonintersectingPaths(PathVector &result, int which, bool inside); + bool _findUnprocessed(IntersectionList::iterator &result); + + PathVector _a, _b; + boost::ptr_vector<IntersectionVertex> _xs; + boost::ptr_vector<PathData> _apaths; + boost::ptr_vector<PathData> _bpaths; + + friend std::ostream &operator<<(std::ostream &, PathIntersectionGraph const &); +}; + +std::ostream &operator<<(std::ostream &os, PathIntersectionGraph const &pig); + +} // namespace Geom + +#endif // SEEN_LIB2GEOM_PATH_GRAPH_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:encoding=utf-8:textwidth=99 : diff --git a/src/2geom/intersection.h b/src/2geom/intersection.h new file mode 100644 index 000000000..bbce19947 --- /dev/null +++ b/src/2geom/intersection.h @@ -0,0 +1,147 @@ +/** + * \file + * \brief Intersection utilities + *//* + * Authors: + * Krzysztof Kosiński <tweenk.pl@gmail.com> + * + * Copyright 2015 Authors + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + */ + +#ifndef SEEN_LIB2GEOM_INTERSECTION_H +#define SEEN_LIB2GEOM_INTERSECTION_H + +#include <2geom/coord.h> +#include <2geom/point.h> + +namespace Geom { + + +/** @brief Intersection between two shapes. + */ +template <typename TimeA = Coord, typename TimeB = TimeA> +class Intersection + : boost::totally_ordered< Intersection<TimeA, TimeB> > +{ +public: + /** @brief Construct from shape references and time values. + * By default, the intersection point will be halfway between the evaluated + * points on the two shapes. */ + template <typename TA, typename TB> + Intersection(TA const &sa, TB const &sb, TimeA const &ta, TimeB const &tb) + : first(ta) + , second(tb) + , _point(lerp(0.5, sa.pointAt(ta), sb.pointAt(tb))) + {} + + /// Additionally report the intersection point. + Intersection(TimeA const &ta, TimeB const &tb, Point const &p) + : first(ta) + , second(tb) + , _point(p) + {} + + /// Intersection point, as calculated by the intersection algorithm. + Point point() const { + return _point; + } + /// Implicit conversion to Point. + operator Point() const { + return _point; + } + + friend inline void swap(Intersection &a, Intersection &b) { + using std::swap; + swap(a.first, b.first); + swap(a.second, b.second); + swap(a._point, b._point); + } + + bool operator==(Intersection const &other) const { + if (first != other.first) return false; + if (second != other.second) return false; + return true; + } + bool operator<(Intersection const &other) const { + if (first < other.first) return true; + if (first == other.first && second < other.second) return true; + return false; + } + +public: + /// First shape and time value. + TimeA first; + /// Second shape and time value. + TimeB second; +private: + // Recalculation of the intersection point from the time values is in many cases + // less precise than the value obtained directly from the intersection algorithm, + // so we need to store it. + Point _point; +}; + + +// TODO: move into new header? +template <typename T> +struct ShapeTraits { + typedef Coord TimeType; + typedef Interval IntervalType; + typedef T AffineClosureType; + typedef Intersection<> IntersectionType; +}; + +template <typename A, typename B> inline +std::vector< Intersection<A, B> > transpose(std::vector< Intersection<B, A> > const &in) { + std::vector< Intersection<A, B> > result; + for (std::size_t i = 0; i < in.size(); ++i) { + result.push_back(Intersection<A, B>(in[i].second, in[i].first, in[i].point())); + } + return result; +} + +template <typename T> inline +void transpose_in_place(std::vector< Intersection<T, T> > &xs) { + for (std::size_t i = 0; i < xs.size(); ++i) { + std::swap(xs[i].first, xs[i].second); + } +} + +typedef Intersection<> ShapeIntersection; + + +} // namespace Geom + +#endif // SEEN_LIB2GEOM_INTERSECTION_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:encoding=utf-8:textwidth=99 : diff --git a/src/2geom/interval.h b/src/2geom/interval.h index 1714435be..fd446a1c6 100644 --- a/src/2geom/interval.h +++ b/src/2geom/interval.h @@ -91,19 +91,28 @@ public: } /// @} - /// @name Inspect endpoints. + /// @name Inspect contained values. /// @{ - /** @brief Access endpoints by value. - * @deprecated Use min() and max() instead */ - Coord operator[](unsigned i) const { return _b[i]; } - /** @brief Access endpoints by reference. - * @deprecated Use min() and max() instead - * @todo Remove Interval index operator, which can be used to break the invariant */ - Coord& operator[](unsigned i) { return _b[i]; } - + /// Check whether both endpoints are finite. bool isFinite() const { return IS_FINITE(min()) && IS_FINITE(max()); } + /** @brief Map the interval [0,1] onto this one. + * This method simply performs 1D linear interpolation between endpoints. */ + Coord valueAt(Coord t) { + return lerp(t, min(), max()); + } + /** @brief Compute a time value that maps to the given value. + * The supplied value does not need to be in the interval for this method to work. */ + Coord timeAt(Coord v) { + return (v - min()) / extent(); + } + /// Find closest time in [0,1] that maps to the given value. */ + Coord nearestTime(Coord v) { + if (v <= min()) return 0; + if (v >= max()) return 1; + return timeAt(v); + } /// @} /// @name Test coordinates and other intervals for inclusion. @@ -114,7 +123,16 @@ public: /** @brief Check whether the interior of the interval includes the given interval. * Interior means all numbers in the interval except its ends. */ bool interiorContains(Interval const &val) const { return min() < val.min() && val.max() < max(); } - /** @brief Check whether the interiors of the intervals have any common elements. A single point in common is not considered an intersection. */ + /// Check whether the number is contained in the union of the interior and the lower boundary. + bool lowerContains(Coord val) { return min() <= val && val < max(); } + /// Check whether the given interval is contained in the union of the interior and the lower boundary. + bool lowerContains(Interval const &val) const { return min() <= val.min() && val.max() < max(); } + /// Check whether the number is contained in the union of the interior and the upper boundary. + bool upperContains(Coord val) { return min() < val && val <= max(); } + /// Check whether the given interval is contained in the union of the interior and the upper boundary. + bool upperContains(Interval const &val) const { return min() < val.min() && val.max() <= max(); } + /** @brief Check whether the interiors of the intervals have any common elements. + * A single point in common is not considered an intersection. */ bool interiorIntersects(Interval const &val) const { return std::max(min(), val.min()) < std::min(max(), val.max()); } @@ -125,16 +143,18 @@ public: // IMPL: ScalableConcept /** @brief Scale an interval */ Interval &operator*=(Coord s) { + using std::swap; _b[0] *= s; _b[1] *= s; - if(s < 0) std::swap(_b[0], _b[1]); + if(s < 0) swap(_b[0], _b[1]); return *this; } /** @brief Scale an interval by the inverse of the specified value */ Interval &operator/=(Coord s) { + using std::swap; _b[0] /= s; _b[1] /= s; - if(s < 0) std::swap(_b[0], _b[1]); + if(s < 0) swap(_b[0], _b[1]); return *this; } /** @brief Multiply two intervals. diff --git a/src/2geom/line.cpp b/src/2geom/line.cpp index 097365245..bada8ef38 100644 --- a/src/2geom/line.cpp +++ b/src/2geom/line.cpp @@ -28,11 +28,9 @@ * the specific language governing rights and limitations. */ - -#include <2geom/line.h> - #include <algorithm> - +#include <2geom/line.h> +#include <2geom/math-utils.h> namespace Geom { @@ -41,12 +39,18 @@ namespace Geom * @class Line * @brief Infinite line on a plane. * - * Every line in 2Geom has a special point on it, called the origin. The direction of the line - * is stored as a unit vector (versor). This way a line can be interpreted as a function - * \f$ f: (-\infty, \infty) \to \mathbb{R}^2\f$. Zero corresponds to the origin point, - * positive values to the points in the direction of the unit vector, and negative values - * to points in the opposite direction. - * + * A line is specified as two points through which it passes. Lines can be interpreted as functions + * \f$ f: (-\infty, \infty) \to \mathbb{R}^2\f$. Zero corresponds to the first (origin) point, + * one corresponds to the second (final) point. All other points are computed as a linear + * interpolation between those two: \f$p = (1-t) a + t b\f$. Many such functions have the same + * image and therefore represent the same lines; for example, adding \f$b-a\f$ to both points + * yields the same line. + * + * 2Geom can represent the same line in many ways by design: using a different representation + * would lead to precision loss. For example, a line from (1e30, 1e30) to (10,0) would actually + * evaluate to (0,0) at time 1 if it was stored as origin and normalized versor, + * or origin and angle. + * * @ingroup Primitives */ @@ -54,36 +58,69 @@ namespace Geom * A line is a set of points that satisfies the line equation * \f$Ax + By + C = 0\f$. This function changes the line so that its points * satisfy the line equation with the given coefficients. */ -void Line::setCoefficients (double a, double b, double c) { +void Line::setCoefficients (Coord a, Coord b, Coord c) +{ + // degenerate case if (a == 0 && b == 0) { if (c != 0) { - THROW_LOGICALERROR("the passed coefficients gives the empty set"); + THROW_LOGICALERROR("the passed coefficients give the empty set"); } - m_versor = Point(0,0); - m_origin = Point(0,0); - } else { - double l = hypot(a,b); - a /= l; - b /= l; - c /= l; - Point N(a, b); - m_versor = N.ccw(); - m_origin = -c * N; + _initial = Point(0,0); + _final = Point(0,0); + return; } + + // The way final / initial points are set based on coefficients is somewhat unusual. + // This is done to make sure that calling coefficients() will give back + // (almost) the same values. + + // vertical case + if (a == 0) { + // b must be nonzero + _initial = Point(-b/2, -c / b); + _final = _initial; + _final[X] = b/2; + return; + } + + // horizontal case + if (b == 0) { + _initial = Point(-c / a, a/2); + _final = _initial; + _final[Y] = -a/2; + return; + } + + // This gives reasonable results regardless of the magnitudes of a, b and c. + _initial = Point(-b/2,a/2); + _final = Point(b/2,-a/2); + + Point offset(-c/(2*a), -c/(2*b)); + + _initial += offset; + _final += offset; +} + +void Line::coefficients(Coord &a, Coord &b, Coord &c) const +{ + Point v = versor().cw(); + a = v[X]; + b = v[Y]; + c = cross(_initial, _final); } -/** @brief Get the line equation coefficients of this line. +/** @brief Get the implicit line equation coefficients. + * Note that conversion to implicit form always causes loss of + * precision when dealing with lines that start far from the origin + * and end very close to it. It is recommended to normalize the line + * before converting it to implicit form. * @return Vector with three values corresponding to the A, B and C * coefficients of the line equation for this line. */ -std::vector<double> Line::coefficients() const { - std::vector<double> coeff; - coeff.reserve(3); - Point N = versor().cw(); - coeff.push_back (N[X]); - coeff.push_back (N[Y]); - double d = - dot (N, origin()); - coeff.push_back (d); - return coeff; +std::vector<Coord> Line::coefficients() const +{ + std::vector<Coord> c(3); + coefficients(c[0], c[1], c[2]); + return c; } /** @brief Find intersection with an axis-aligned line. @@ -91,87 +128,213 @@ std::vector<double> Line::coefficients() const { * @param d Which axis the coordinate is on. X means a vertical line, Y means a horizontal line. * @return Time values at which this line intersects the query line. */ std::vector<Coord> Line::roots(Coord v, Dim2 d) const { - if (d < 0 || d > 1) - THROW_RANGEERROR("Line::roots, dimension argument out of range"); std::vector<Coord> result; - if ( m_versor[d] != 0 ) - { - result.push_back( (v - m_origin[d]) / m_versor[d] ); + Coord r = root(v, d); + if (IS_FINITE(r)) { + result.push_back(r); } - // TODO: else ? return result; } +Coord Line::root(Coord v, Dim2 d) const +{ + assert(d == X || d == Y); + Point vs = versor(); + if (vs[d] != 0) { + return (v - _initial[d]) / vs[d]; + } else { + return nan(""); + } +} + +boost::optional<LineSegment> Line::clip(Rect const &r) const +{ + Point v = versor(); + // handle horizontal and vertical lines first, + // since the root-based code below will break for them + for (unsigned i = 0; i < 2; ++i) { + Dim2 d = (Dim2) i; + Dim2 o = other_dimension(d); + if (v[d] != 0) continue; + if (r[d].contains(_initial[d])) { + Point a, b; + a[o] = r[o].min(); + b[o] = r[o].max(); + a[d] = b[d] = _initial[d]; + if (v[o] > 0) { + return LineSegment(a, b); + } else { + return LineSegment(b, a); + } + } else { + return boost::none; + } + } + + Interval xpart(root(r[X].min(), X), root(r[X].max(), X)); + Interval ypart(root(r[Y].min(), Y), root(r[Y].max(), Y)); + if (!xpart.isFinite() || !ypart.isFinite()) { + return boost::none; + } + + OptInterval common = xpart & ypart; + if (common) { + Point p1 = pointAt(common->min()), p2 = pointAt(common->max()); + LineSegment result(r.clamp(p1), r.clamp(p2)); + return result; + } else { + return boost::none; + } + + /* old implementation using coefficients: + + if (fabs(b) > fabs(a)) { + p0 = Point(r[X].min(), (-c - a*r[X].min())/b); + if (p0[Y] < r[Y].min()) + p0 = Point((-c - b*r[Y].min())/a, r[Y].min()); + if (p0[Y] > r[Y].max()) + p0 = Point((-c - b*r[Y].max())/a, r[Y].max()); + p1 = Point(r[X].max(), (-c - a*r[X].max())/b); + if (p1[Y] < r[Y].min()) + p1 = Point((-c - b*r[Y].min())/a, r[Y].min()); + if (p1[Y] > r[Y].max()) + p1 = Point((-c - b*r[Y].max())/a, r[Y].max()); + } else { + p0 = Point((-c - b*r[Y].min())/a, r[Y].min()); + if (p0[X] < r[X].min()) + p0 = Point(r[X].min(), (-c - a*r[X].min())/b); + if (p0[X] > r[X].max()) + p0 = Point(r[X].max(), (-c - a*r[X].max())/b); + p1 = Point((-c - b*r[Y].max())/a, r[Y].max()); + if (p1[X] < r[X].min()) + p1 = Point(r[X].min(), (-c - a*r[X].min())/b); + if (p1[X] > r[X].max()) + p1 = Point(r[X].max(), (-c - a*r[X].max())/b); + } + return LineSegment(p0, p1); */ +} + /** @brief Get a time value corresponding to a point. * @param p Point on the line. If the point is not on the line, * the returned value will be meaningless. * @return Time value t such that \f$f(t) = p\f$. * @see timeAtProjection */ -Coord Line::timeAt(Point const& _point) const { - Coord t; - if ( m_versor[X] != 0 ) { - t = (_point[X] - m_origin[X]) / m_versor[X]; +Coord Line::timeAt(Point const &p) const +{ + Point v = versor(); + // degenerate case + if (v[X] == 0 && v[Y] == 0) { + return 0; } - else if ( m_versor[Y] != 0 ) { - t = (_point[Y] - m_origin[Y]) / m_versor[Y]; + + // use the coordinate that will give better precision + if (fabs(v[X]) > fabs(v[Y])) { + return (p[X] - _initial[X]) / v[X]; + } else { + return (p[Y] - _initial[Y]) / v[Y]; } - else { // degenerate case - t = 0; +} + +std::vector<ShapeIntersection> Line::intersect(Line const &other) const +{ + std::vector<ShapeIntersection> result; + + Point v1 = versor(); + Point v2 = other.versor(); + Coord cp = cross(v1, v2); + if (cp == 0) return result; + + Point odiff = other.initialPoint() - initialPoint(); + Coord t1 = cross(odiff, v2) / cp; + Coord t2 = cross(odiff, v1) / cp; + result.push_back(ShapeIntersection(*this, other, t1, t2)); + return result; +} + +std::vector<ShapeIntersection> Line::intersect(Ray const &r) const +{ + Line other(r); + std::vector<ShapeIntersection> result = intersect(other); + filter_ray_intersections(result, false, true); + return result; +} + +std::vector<ShapeIntersection> Line::intersect(LineSegment const &ls) const +{ + Line other(ls); + std::vector<ShapeIntersection> result = intersect(other); + filter_line_segment_intersections(result, false, true); + return result; +} + + + +void filter_line_segment_intersections(std::vector<ShapeIntersection> &xs, bool a, bool b) +{ + Interval unit(0, 1); + std::vector<ShapeIntersection>::reverse_iterator i = xs.rbegin(), last = xs.rend(); + while (i != last) { + if ((a && !unit.contains(i->first)) || (b && !unit.contains(i->second))) { + xs.erase((++i).base()); + } else { + ++i; + } + } +} + +void filter_ray_intersections(std::vector<ShapeIntersection> &xs, bool a, bool b) +{ + Interval unit(0, 1); + std::vector<ShapeIntersection>::reverse_iterator i = xs.rbegin(), last = xs.rend(); + while (i != last) { + if ((a && i->first < 0) || (b && i->second < 0)) { + xs.erase((++i).base()); + } else { + ++i; + } } - return t; } namespace detail { inline -OptCrossing intersection_impl(Point const& V1, Point const O1, - Point const& V2, Point const O2 ) +OptCrossing intersection_impl(Point const &v1, Point const &o1, + Point const &v2, Point const &o2) { - double detV1V2 = V1[X] * V2[Y] - V2[X] * V1[Y]; - if (are_near(detV1V2, 0)) return OptCrossing(); + Coord cp = cross(v1, v2); + if (cp == 0) return OptCrossing(); - Point B = O2 - O1; - double detBV2 = B[X] * V2[Y] - V2[X] * B[Y]; - double detV1B = B[X] * V1[Y] - V1[X] * B[Y]; - double inv_detV1V2 = 1 / detV1V2; + Point odiff = o2 - o1; Crossing c; - c.ta = detBV2 * inv_detV1V2; - c.tb = detV1B * inv_detV1V2; -// std::cerr << "ta = " << c.ta << std::endl; -// std::cerr << "tb = " << c.tb << std::endl; - return OptCrossing(c); + c.ta = cross(odiff, v2) / cp; + c.tb = cross(odiff, v1) / cp; + return c; } OptCrossing intersection_impl(Ray const& r1, Line const& l2, unsigned int i) { + using std::swap; + OptCrossing crossing = intersection_impl(r1.versor(), r1.origin(), l2.versor(), l2.origin() ); - if (crossing) - { - if (crossing->ta < 0) - { + if (crossing) { + if (crossing->ta < 0) { return OptCrossing(); - } - else - { - if (i != 0) - { - std::swap(crossing->ta, crossing->tb); + } else { + if (i != 0) { + swap(crossing->ta, crossing->tb); } return crossing; } } - if (are_near(r1.origin(), l2)) - { + if (are_near(r1.origin(), l2)) { THROW_INFINITESOLUTIONS(); - } - else - { + } else { return OptCrossing(); } } @@ -181,34 +344,29 @@ OptCrossing intersection_impl( LineSegment const& ls1, Line const& l2, unsigned int i ) { + using std::swap; + OptCrossing crossing = intersection_impl(ls1.finalPoint() - ls1.initialPoint(), ls1.initialPoint(), l2.versor(), l2.origin() ); - if (crossing) - { + if (crossing) { if ( crossing->getTime(0) < 0 || crossing->getTime(0) > 1 ) { return OptCrossing(); - } - else - { - if (i != 0) - { - std::swap((*crossing).ta, (*crossing).tb); + } else { + if (i != 0) { + swap((*crossing).ta, (*crossing).tb); } return crossing; } } - if (are_near(ls1.initialPoint(), l2)) - { + if (are_near(ls1.initialPoint(), l2)) { THROW_INFINITESOLUTIONS(); - } - else - { + } else { return OptCrossing(); } } @@ -218,6 +376,8 @@ OptCrossing intersection_impl( LineSegment const& ls1, Ray const& r2, unsigned int i ) { + using std::swap; + Point direction = ls1.finalPoint() - ls1.initialPoint(); OptCrossing crossing = intersection_impl( direction, @@ -225,57 +385,40 @@ OptCrossing intersection_impl( LineSegment const& ls1, r2.versor(), r2.origin() ); - if (crossing) - { + if (crossing) { if ( (crossing->getTime(0) < 0) || (crossing->getTime(0) > 1) || (crossing->getTime(1) < 0) ) { return OptCrossing(); - } - else - { - if (i != 0) - { - std::swap(crossing->ta, crossing->tb); + } else { + if (i != 0) { + swap(crossing->ta, crossing->tb); } return crossing; } } - if ( are_near(r2.origin(), ls1) ) - { + if ( are_near(r2.origin(), ls1) ) { bool eqvs = (dot(direction, r2.versor()) > 0); - if ( are_near(ls1.initialPoint(), r2.origin()) && !eqvs ) - { + if ( are_near(ls1.initialPoint(), r2.origin()) && !eqvs) { crossing->ta = crossing->tb = 0; return crossing; - } - else if ( are_near(ls1.finalPoint(), r2.origin()) && eqvs ) - { - if (i == 0) - { + } else if ( are_near(ls1.finalPoint(), r2.origin()) && eqvs) { + if (i == 0) { crossing->ta = 1; crossing->tb = 0; - } - else - { + } else { crossing->ta = 0; crossing->tb = 1; } return crossing; - } - else - { + } else { THROW_INFINITESOLUTIONS(); } - } - else if ( are_near(ls1.initialPoint(), r2) ) - { + } else if ( are_near(ls1.initialPoint(), r2) ) { THROW_INFINITESOLUTIONS(); - } - else - { + } else { OptCrossing no_crossing; return no_crossing; } @@ -287,24 +430,16 @@ OptCrossing intersection_impl( LineSegment const& ls1, OptCrossing intersection(Line const& l1, Line const& l2) { - OptCrossing crossing = - detail::intersection_impl( l1.versor(), l1.origin(), - l2.versor(), l2.origin() ); - if (crossing) - { - return crossing; - } - if (are_near(l1.origin(), l2)) - { + OptCrossing c = detail::intersection_impl( + l1.versor(), l1.origin(), + l2.versor(), l2.origin()); + + if (!c && distance(l1.origin(), l2) == 0) { THROW_INFINITESOLUTIONS(); } - else - { - return crossing; - } + return c; } - OptCrossing intersection(Ray const& r1, Ray const& r2) { OptCrossing crossing = @@ -416,64 +551,6 @@ OptCrossing intersection( LineSegment const& ls1, LineSegment const& ls2 ) } } - -boost::optional<LineSegment> clip (Line const& l, Rect const& r) -{ - typedef boost::optional<LineSegment> opt_linesegment; - LineSegment result; - //size_t index = 0; - std::vector<Point> points; - LineSegment ls (r.corner(0), r.corner(1)); - try - { - OptCrossing oc = intersection (ls, l); - if (oc) - { - points.push_back (l.pointAt (oc->tb)); - } - } - catch (InfiniteSolutions const &e) - { - return opt_linesegment(ls); - } - - for (size_t i = 2; i < 5; ++i) - { - ls.setInitial (ls[1]); - ls.setFinal (r.corner(i)); - try - { - OptCrossing oc = intersection (ls, l); - if (oc) - { - points.push_back (l.pointAt (oc->tb)); - if (points.size() > 1) - { - size_t sz = points.size(); - if (!are_near (points[sz - 2], points[sz - 1], 1e-10)) - { - result.setInitial (points[sz - 2]); - result.setFinal (points[sz - 1]); - return opt_linesegment(result); - } - } - } - } - catch (InfiniteSolutions const &e) - { - return opt_linesegment(ls); - } - } - if ( !points.empty() ) - { - result.setInitial (points[0]); - result.setFinal (points[0]); - return opt_linesegment(result); - } - return opt_linesegment(); -} - - Line make_angle_bisector_line(Line const& l1, Line const& l2) { OptCrossing crossing; diff --git a/src/2geom/line.h b/src/2geom/line.h index cbd68fa08..7d4766e12 100644 --- a/src/2geom/line.h +++ b/src/2geom/line.h @@ -35,7 +35,6 @@ #define LIB2GEOM_SEEN_LINE_H #include <cmath> -#include <iostream> #include <boost/optional.hpp> #include <2geom/bezier-curve.h> // for LineSegment #include <2geom/rect.h> @@ -43,36 +42,46 @@ #include <2geom/exception.h> #include <2geom/ray.h> #include <2geom/angle.h> +#include <2geom/intersection.h> namespace Geom { -class Line { +// class docs in cpp file +class Line + : boost::equality_comparable< Line > +{ private: - Point m_origin; - Point m_versor; + Point _initial; + Point _final; public: /// @name Creating lines. /// @{ - /** @brief Create a default horizontal line. */ + /** @brief Create a default horizontal line. + * Creates a line with unit speed going in +X direction. */ Line() - : m_origin(0,0), m_versor(1,0) + : _initial(0,0), _final(1,0) {} /** @brief Create a line with the specified inclination. - * @param _origin One of the points on the line + * @param origin One of the points on the line * @param angle Angle of the line in mathematical convention */ - Line(Point const& _origin, Coord angle ) - : m_origin(_origin) + Line(Point const &origin, Coord angle) + : _initial(origin) { - sincos(angle, m_versor[Y], m_versor[X]); + Point v; + sincos(angle, v[Y], v[X]); + _final = _initial + v; } /** @brief Create a line going through two points. - * @param A First point - * @param B Second point */ - Line(Point const& A, Point const& B) { - setPoints(A, B); - } + * The first point will be at time 0, while the second one + * will be at time 1. + * @param a Initial point + * @param b First point */ + Line(Point const &a, Point const &b) + : _initial(a) + , _final(b) + {} /** @brief Create a line based on the coefficients of its equation. @see Line::setCoefficients() */ @@ -80,30 +89,30 @@ public: setCoefficients(a, b, c); } - /** @brief Create a line by extending a line segment. */ - explicit Line(LineSegment const& _segment) { - setPoints(_segment.initialPoint(), _segment.finalPoint()); - } + /// Create a line by extending a line segment. + explicit Line(LineSegment const &seg) + : _initial(seg.initialPoint()) + , _final(seg.finalPoint()) + {} - /** @brief Create a line by extending a ray. */ - explicit Line(Ray const& _ray) - : m_origin(_ray.origin()), m_versor(_ray.versor()) + /// Create a line by extending a ray. + explicit Line(Ray const &r) + : _initial(r.origin()) + , _final(r.origin() + r.versor()) {} - // huh? - static Line from_normal_distance(Point n, double c) { - Point P = n * c / dot(n,n); - Line l(P, P+rot90(n)); + /// Create a line normal to a vector at a specified distance from origin. + static Line from_normal_distance(Point const &n, Coord c) { + Point start = c * n.normalized(); + Line l(start, start + rot90(n)); return l; } /** @brief Create a line from origin and unit vector. * Note that each line direction has two possible unit vectors. * @param o Point through which the line will pass * @param v Unit vector of the line's direction */ - static Line from_origin_and_versor(Point o, Point v) { - Line l; - l.m_origin = o; - l.m_versor = v; + static Line from_origin_and_versor(Point const &o, Point const &v) { + Line l(o, o + v); return l; } @@ -114,63 +123,113 @@ public: /// @name Retrieve and set the line's parameters. /// @{ - /** @brief Get the line's origin point. */ - Point origin() const { return m_origin; } - /** @brief Get the line's direction unit vector. */ - Point versor() const { return m_versor; } - // return the angle described by rotating the X-axis in cw direction - // until it overlaps the line - // the returned value is in the interval [0, PI[ + + /// Get the line's origin point. + Point origin() const { return _initial; } + /** @brief Get the line's direction vector. + * Note that the retrieved vector is not normalized to unit length. */ + Point versor() const { return _final - _initial; } + /// Angle the line makes with the X axis, in mathematical convention. Coord angle() const { - double a = std::atan2(m_versor[Y], m_versor[X]); + Point d = _final - _initial; + double a = std::atan2(d[Y], d[X]); if (a < 0) a += M_PI; if (a == M_PI) a = 0; return a; } - void setOrigin(Point const& _point) { - m_origin = _point; + /** @brief Set the point at zero time. + * The orientation remains unchanged, modulo numeric errors during addition. */ + void setOrigin(Point const &p) { + Point d = p - _initial; + _initial = p; + _final += d; } - void setVersor(Point const& _versor) { - m_versor = _versor; + /** @brief Set the speed of the line. + * Origin remains unchanged. */ + void setVersor(Point const &v) { + _final = _initial + v; } - void setAngle(Coord _angle) { - sincos(_angle, m_versor[Y], m_versor[X]); + /** @brief Set the angle the line makes with the X axis. + * Origin remains unchanged. */ + void setAngle(Coord angle) { + Point v; + sincos(angle, v[Y], v[X]); + v *= distance(_initial, _final); + _final = _initial + v; } - /** @brief Set a line based on two points it should pass through. */ - void setPoints(Point const& A, Point const& B) { - m_origin = A; - if ( are_near(A, B) ) - m_versor = Point(0,0); - else - m_versor = B - A; - m_versor.normalize(); + /// Set a line based on two points it should pass through. + void setPoints(Point const &a, Point const &b) { + _initial = a; + _final = b; } - void setCoefficients (double a, double b, double c); + /** @brief Set the coefficients of the line equation. + * The line equation is: \f$ax + by = c\f$. Points that satisfy the equation + * are on the line. */ + void setCoefficients(double a, double b, double c); + + /** @brief Get the coefficients of the line equation as a vector. + * @return STL vector @a v such that @a v[0] contains \f$a\f$, @a v[1] contains \f$b\f$, + * and @a v[2] contains \f$c\f$. */ std::vector<double> coefficients() const; - /** @brief Check if the line has any points. + /// Get the coefficients of the line equation by reference. + void coefficients(Coord &a, Coord &b, Coord &c) const; + + /** @brief Check if the line has more than one point. * A degenerate line can be created if the line is created from a line equation * that has no solutions. - * @return True if the line has no points */ + * @return True if the line has no points or exactly one point */ bool isDegenerate() const { - return ( m_versor[X] == 0 && m_versor[Y] == 0 ); + return _initial == _final; + } + /// Check if the line is horizontal (y is constant). + bool isHorizontal() const { + return _initial[Y] == _final[Y]; + } + /// Check if the line is vertical (x is constant). + bool isVertical() const { + return _initial[X] == _final[X]; + } + + /** @brief Reparametrize the line so that it has unit speed. + * Note that the direction of the line may also change. */ + void normalize() { + // this helps with the nasty case of a line that starts somewhere far + // and ends very close to the origin + if (L2sq(_final) < L2sq(_initial)) { + std::swap(_initial, _final); + } + Point v = _final - _initial; + v.normalize(); + _final = _initial + v; + } + /** @brief Return a new line reparametrized for unit speed. */ + Line normalized() const { + Point v = _final - _initial; + v.normalize(); + Line ret(_initial, _initial + v); + return ret; } /// @} /// @name Evaluate the line as a function. ///@{ + Point initialPoint() const { + return _initial; + } + Point finalPoint() const { + return _final; + } Point pointAt(Coord t) const { - return m_origin + m_versor * t; + return lerp(t, _initial, _final);; } Coord valueAt(Coord t, Dim2 d) const { - if (d < 0 || d > 1) - THROW_RANGEERROR("Line::valueAt, dimension argument out of range"); - return m_origin[d] + m_versor[d] * t; + return lerp(t, _initial[d], _final[d]); } Coord timeAt(Point const &p) const; @@ -180,27 +239,29 @@ public: * @return Time value corresponding to a point closest to @c p. */ Coord timeAtProjection(Point const& p) const { if ( isDegenerate() ) return 0; - return dot( p - m_origin, m_versor ); + Point v = versor(); + return dot(p - _initial, v) / dot(v, v); } /** @brief Find a point on the line closest to the query point. * This is an alias for timeAtProjection(). */ - Coord nearestPoint(Point const& _point) const { - return timeAtProjection(_point); + Coord nearestTime(Point const &p) const { + return timeAtProjection(p); } std::vector<Coord> roots(Coord v, Dim2 d) const; + Coord root(Coord v, Dim2 d) const; /// @} /// @name Create other objects based on this line. /// @{ - /** @brief Create a line containing the same points, but with negated time values. - * @return Line \f$g\f$ such that \f$g(t) = f(-t)\f$ */ - Line reverse() const - { - Line result; - result.setOrigin(m_origin); - result.setVersor(-m_versor); + void reverse() { + std::swap(_final, _initial); + } + /** @brief Create a line containing the same points, but in opposite direction. + * @return Line \f$g\f$ such that \f$g(t) = f(1-t)\f$ */ + Line reversed() const { + Line result(_final, _initial); return result; } @@ -219,6 +280,9 @@ public: return LineSegment(pointAt(f), pointAt(t)); } + /// Return the portion of the line that is inside the given rectangle + boost::optional<LineSegment> clip(Rect const &r) const; + /** @brief Create a ray starting at the specified time value. * The created ray will go in the direction of the line's versor (in the direction * of increasing time values). @@ -227,7 +291,7 @@ public: Ray ray(Coord t) { Ray result; result.setOrigin(pointAt(t)); - result.setVersor(m_versor); + result.setVersor(versor()); return result; } @@ -235,88 +299,148 @@ public: * The new line will always be degenerate. Its origin will be equal to this * line's versor. */ Line derivative() const { - Line result; - result.setOrigin(m_versor); - result.setVersor(Point(0,0)); + Point v = versor(); + Line result(v, v); return result; } - /** @brief Create a line transformed by an affine transformation. */ + /// Create a line transformed by an affine transformation. Line transformed(Affine const& m) const { - return Line(m_origin * m, (m_origin + m_versor) * m); + Line l(_initial * m, _final * m); + return l; } - /** @brief Get a vector normal to the line. + /** @brief Get a unit vector normal to the line. * If Y grows upwards, then this is the left normal. If Y grows downwards, * then this is the right normal. */ Point normal() const { - return rot90(m_versor); + return rot90(versor()).normalized(); } // what does this do? Point normalAndDist(double & dist) const { Point n = normal(); - dist = -dot(n, m_origin); + dist = -dot(n, _initial); return n; } - friend inline std::ostream &operator<< (std::ostream &out_file, const Geom::Line &in_line); -/// @} -}; // end class Line + /// Compute an affine matrix representing a reflection about the line. + Affine reflection() const { + Point v = versor().normalized(); + Coord x2 = v[X]*v[X], y2 = v[Y]*v[Y], xy = v[X]*v[Y]; + Affine m(x2-y2, 2.*xy, + 2.*xy, y2-x2, + _initial[X], _initial[Y]); + m = Translate(-_initial) * m; + return m; + } -/** @brief Output operator for lines. - * Prints out representation (point + versor) - */ -inline std::ostream &operator<< (std::ostream &out_file, const Geom::Line &in_line) { - out_file << "X: " << in_line.m_origin[X] << " Y: " << in_line.m_origin[Y] - << " dX: " << in_line.m_versor[X] << " dY: " << in_line.m_versor[Y]; - return out_file; -} + /** @brief Compute an affine which transforms all points on the line to zero X or Y coordinate. + * This operation is useful in reducing intersection problems to root-finding problems. + * There are many affines which do this transformation. This function returns one that + * preserves angles, areas and distances - a rotation combined with a translation, and + * additionaly moves the initial point of the line to (0,0). This way it works without + * problems even for lines perpendicular to the target, though may in some cases have + * lower precision than e.g. a shear transform. + * @param d Which coordinate of points on the line should be zero after the transformation */ + Affine rotationToZero(Dim2 d) const { + Point v = versor(); + if (d == X) { + std::swap(v[X], v[Y]); + } else { + v[Y] = -v[Y]; + } + Affine m = Translate(-_initial) * Rotate(v); + return m; + } + /** @brief Compute a rotation affine which transforms the line to one of the axes. + * @param d Which line should be the axis */ + Affine rotationToAxis(Dim2 d) const { + Affine m = rotationToZero(other_dimension(d)); + return m; + } + /// @} -inline -double distance(Point const& _point, Line const& _line) -{ - if ( _line.isDegenerate() ) - { - return ::Geom::distance( _point, _line.origin() ); + std::vector<ShapeIntersection> intersect(Line const &other) const; + std::vector<ShapeIntersection> intersect(Ray const &r) const; + std::vector<ShapeIntersection> intersect(LineSegment const &ls) const; + + template <typename T> + Line &operator*=(T const &tr) { + BOOST_CONCEPT_ASSERT((TransformConcept<T>)); + _initial *= tr; + _final *= tr; + return *this; } - else - { - return fabs( dot(_point - _line.origin(), _line.versor().ccw()) ); + + bool operator==(Line const &other) const { + if (distance(pointAt(nearestTime(other._initial)), other._initial) != 0) return false; + if (distance(pointAt(nearestTime(other._final)), other._final) != 0) return false; + return true; } -} + template <typename T> + friend Line operator*(Line const &l, T const &tr) { + BOOST_CONCEPT_ASSERT((TransformConcept<T>)); + Line result(l); + result *= tr; + return result; + } +}; // end class Line + +/** @brief Removes intersections outside of the unit interval. + * A helper used to implement line segment intersections. + * @param xs Line intersections + * @param a Whether the first time value has to be in the unit interval + * @param b Whether the second time value has to be in the unit interval + * @return Appropriately filtered intersections */ +void filter_line_segment_intersections(std::vector<ShapeIntersection> &xs, bool a=false, bool b=true); +void filter_ray_intersections(std::vector<ShapeIntersection> &xs, bool a=false, bool b=true); + +/// @brief Compute distance from point to line. +/// @relates Line inline -bool are_near(Point const& _point, Line const& _line, double eps = EPSILON) +double distance(Point const &p, Line const &line) { - return are_near(distance(_point, _line), 0, eps); + if (line.isDegenerate()) { + return ::Geom::distance(p, line.initialPoint()); + } else { + Coord t = line.nearestTime(p); + return ::Geom::distance(line.pointAt(t), p); + } } inline -bool are_parallel(Line const& l1, Line const& l2, double eps = EPSILON) +bool are_near(Point const &p, Line const &line, double eps = EPSILON) { - return ( are_near(l1.versor(), l2.versor(), eps) - || are_near(l1.versor(), -l2.versor(), eps) ); + return are_near(distance(p, line), 0, eps); } inline -bool are_same(Line const& l1, Line const& l2, double eps = EPSILON) +bool are_parallel(Line const &l1, Line const &l2, double eps = EPSILON) { - return are_parallel(l1, l2, eps) && are_near(l1.origin(), l2, eps); + return are_near(cross(l1.versor(), l2.versor()), 0, eps); } +/** @brief Test whether two lines are approximately the same. + * This tests for being parallel and the origin of one line being close to the other, + * so it tests whether the images of the lines are similar, not whether the same time values + * correspond to similar points. For example a line from (1,1) to (2,2) and a line from + * (-1,-1) to (0,0) will the the same, because their images match, even though there is + * no time value for which the lines give similar points. + * @relates Line */ inline -bool are_orthogonal(Line const& l1, Line const& l2, double eps = EPSILON) +bool are_same(Line const &l1, Line const &l2, double eps = EPSILON) { - return ( are_near(l1.versor(), l2.versor().cw(), eps) - || are_near(l1.versor(), l2.versor().ccw(), eps) ); + return are_parallel(l1, l2, eps) && are_near(l1.origin(), l2, eps); } +/// Test whether two lines are perpendicular. +/// @relates Line inline -bool are_collinear(Point const& p1, Point const& p2, Point const& p3, - double eps = EPSILON) +bool are_orthogonal(Line const &l1, Line const &l2, double eps = EPSILON) { - return are_near( cross(p3, p2) - cross(p3, p1) + cross(p2, p1), 0, eps); + return are_near(dot(l1.versor(), l2.versor()), 0, eps); } // evaluate the angle between l1 and l2 rotating l1 in cw direction @@ -332,36 +456,34 @@ double angle_between(Line const& l1, Line const& l2) } inline -double distance(Point const& _point, LineSegment const& _segment) +double distance(Point const &p, LineSegment const &seg) { - double t = _segment.nearestPoint(_point); - return L2(_point - _segment.pointAt(t)); + double t = seg.nearestTime(p); + return distance(p, seg.pointAt(t)); } inline -bool are_near(Point const& _point, LineSegment const& _segment, - double eps = EPSILON) +bool are_near(Point const &p, LineSegment const &seg, double eps = EPSILON) { - return are_near(distance(_point, _segment), 0, eps); + return are_near(distance(p, seg), 0, eps); } // build a line passing by _point and orthogonal to _line inline -Line make_orthogonal_line(Point const& _point, Line const& _line) +Line make_orthogonal_line(Point const &p, Line const &line) { - Line l; - l.setOrigin(_point); - l.setVersor(_line.versor().cw()); + Point d = line.versor().cw(); + Line l(p, p + d); return l; } // build a line passing by _point and parallel to _line inline -Line make_parallel_line(Point const& _point, Line const& _line) +Line make_parallel_line(Point const &p, Line const &line) { - Line l(_line); - l.setOrigin(_point); - return l; + Line result(line); + result.setOrigin(p); + return result; } // build a line passing by the middle point of _segment and orthogonal to it. @@ -373,31 +495,31 @@ Line make_bisector_line(LineSegment const& _segment) // build the bisector line of the angle between ray(O,A) and ray(O,B) inline -Line make_angle_bisector_line(Point const& A, Point const& O, Point const& B) +Line make_angle_bisector_line(Point const &A, Point const &O, Point const &B) { - Point M = middle_point(A,B); - if (are_near(O,M)) { - Line l(A,B); - M += (make_orthogonal_line(O,l)).versor(); - } - return Line(O,M); + AngleInterval ival(Angle(A-O), Angle(B-O)); + Angle bisect = ival.angleAt(0.5); + return Line(O, bisect); } // prj(P) = rot(v, Point( rot(-v, P-O)[X], 0 )) + O inline -Point projection(Point const& _point, Line const& _line) +Point projection(Point const &p, Line const &line) { - return _line.pointAt( _line.nearestPoint(_point) ); + return line.pointAt(line.nearestTime(p)); } inline -LineSegment projection(LineSegment const& _segment, Line const& _line) +LineSegment projection(LineSegment const &seg, Line const &line) { - return _line.segment( _line.nearestPoint(_segment.initialPoint()), - _line.nearestPoint(_segment.finalPoint()) ); + return line.segment(line.nearestTime(seg.initialPoint()), + line.nearestTime(seg.finalPoint())); } -boost::optional<LineSegment> clip (Line const& l, Rect const& r); +inline +boost::optional<LineSegment> clip(Line const &l, Rect const &r) { + return l.clip(r); +} namespace detail diff --git a/src/2geom/linear.h b/src/2geom/linear.h index 8c154364e..b0306fb9f 100644 --- a/src/2geom/linear.h +++ b/src/2geom/linear.h @@ -1,7 +1,7 @@ /** * \file * \brief Linear fragment function class - * + *//* * Authors: * Nathan Hurst <njh@mail.csse.monash.edu.au> * Michael Sloan <mgsloan@gmail.com> @@ -32,12 +32,12 @@ * the specific language governing rights and limitations. */ -#ifndef SEEN_LINEAR_H -#define SEEN_LINEAR_H +#ifndef LIB2GEOM_SEEN_LINEAR_H +#define LIB2GEOM_SEEN_LINEAR_H + #include <2geom/interval.h> #include <2geom/math-utils.h> - //#define USE_SBASIS_OF #ifdef USE_SBASIS_OF @@ -46,48 +46,60 @@ #else -namespace Geom{ - -inline double lerp(double t, double a, double b) { return a*(1-t) + b*t; } +namespace Geom { class SBasis; -class Linear{ +/** + * @brief Function that interpolates linearly between two values. + * @ingroup Fragments + */ +class Linear { public: double a[2]; Linear() {a[0]=0; a[1]=0;} Linear(double aa, double b) {a[0] = aa; a[1] = b;} Linear(double aa) {a[0] = aa; a[1] = aa;} - double operator[](const int i) const { - assert(i >= 0); + double operator[](unsigned i) const { assert(i < 2); return a[i]; } - double& operator[](const int i) { - assert(i >= 0); + double &operator[](unsigned i) { assert(i < 2); return a[i]; } //IMPL: FragmentConcept typedef double output_type; - inline bool isZero(double eps=EPSILON) const { return are_near(a[0], 0., eps) && are_near(a[1], 0., eps); } - inline bool isConstant(double eps=EPSILON) const { return are_near(a[0], a[1], eps); } - inline bool isFinite() const { return IS_FINITE(a[0]) && IS_FINITE(a[1]); } - - inline double at0() const { return a[0]; } - inline double at1() const { return a[1]; } - - inline double valueAt(double t) const { return lerp(t, a[0], a[1]); } - inline double operator()(double t) const { return valueAt(t); } + bool isZero(double eps=EPSILON) const { return are_near(a[0], 0., eps) && are_near(a[1], 0., eps); } + bool isConstant(double eps=EPSILON) const { return are_near(a[0], a[1], eps); } + bool isFinite() const { return IS_FINITE(a[0]) && IS_FINITE(a[1]); } + + Coord at0() const { return a[0]; } + Coord &at0() { return a[0]; } + Coord at1() const { return a[1]; } + Coord &at1() { return a[1]; } + + double valueAt(double t) const { return lerp(t, a[0], a[1]); } + double operator()(double t) const { return valueAt(t); } + + // not very useful, but required for ShapeConcept + std::vector<Coord> valueAndDerivatives(Coord t, unsigned n) { + std::vector<Coord> result(n+1, 0.0); + result[0] = valueAt(t); + if (n >= 1) { + result[1] = a[1] - a[0]; + } + return result; + } //defined in sbasis.h inline SBasis toSBasis() const; - inline OptInterval bounds_exact() const { return Interval(a[0], a[1]); } - inline OptInterval bounds_fast() const { return bounds_exact(); } - inline OptInterval bounds_local(double u, double v) const { return Interval(valueAt(u), valueAt(v)); } + OptInterval bounds_exact() const { return Interval(a[0], a[1]); } + OptInterval bounds_fast() const { return bounds_exact(); } + OptInterval bounds_local(double u, double v) const { return Interval(valueAt(u), valueAt(v)); } double tri() const { return a[1] - a[0]; @@ -98,6 +110,10 @@ public: }; inline Linear reverse(Linear const &a) { return Linear(a[1], a[0]); } +inline Linear portion(Linear const &a, Coord from, Coord to) { + Linear result(a.valueAt(from), a.valueAt(to)); + return result; +} //IMPL: AddableConcept inline Linear operator+(Linear const & a, Linear const & b) { @@ -158,7 +174,7 @@ inline Linear operator/=(Linear & a, double b) { } #endif -#endif //SEEN_LINEAR_H +#endif //LIB2GEOM_SEEN_LINEAR_H /* Local Variables: diff --git a/src/2geom/nearest-point.cpp b/src/2geom/nearest-time.cpp index c5dfc133c..103921021 100644 --- a/src/2geom/nearest-point.cpp +++ b/src/2geom/nearest-time.cpp @@ -1,9 +1,8 @@ -/* - * nearest point routines for D2<SBasis> and Piecewise<D2<SBasis>> - * +/** @file + * @brief Nearest time routines for D2<SBasis> and Piecewise<D2<SBasis>> + *//* * Authors: - * - * Marco Cecchetti <mrcekets at gmail.com> + * Marco Cecchetti <mrcekets at gmail.com> * * Copyright 2007-2008 authors * @@ -32,27 +31,79 @@ */ -#include <2geom/nearest-point.h> +#include <2geom/nearest-time.h> #include <algorithm> - namespace Geom { +Coord nearest_time(Point const &p, D2<Bezier> const &input, Coord from, Coord to) +{ + Interval domain(from, to); + bool partial = false; + + if (domain.min() < 0 || domain.max() > 1) { + THROW_RANGEERROR("[from,to] interval out of bounds"); + } + + if (input.isConstant(0)) return from; + + D2<Bezier> bez; + if (domain.min() != 0 || domain.max() != 1) { + bez = portion(input, domain) - p; + partial = true; + } else { + bez = input - p; + } + + // find extrema of the function x(t)^2 + y(t)^2 + // use the fact that (f^2)' = 2 f f' + // this reduces the order of the distance function by 1 + D2<Bezier> deriv = derivative(bez); + std::vector<Coord> ts = (multiply(bez[X], deriv[X]) + multiply(bez[Y], deriv[Y])).roots(); + + Coord t = -1, mind = infinity(); + for (unsigned i = 0; i < ts.size(); ++i) { + Coord droot = L2sq(bez.valueAt(ts[i])); + if (droot < mind) { + mind = droot; + t = ts[i]; + } + } + + // also check endpoints + Coord dinitial = L2sq(bez.at0()); + Coord dfinal = L2sq(bez.at1()); + + if (dinitial < mind) { + mind = dinitial; + t = 0; + } + if (dfinal < mind) { + //mind = dfinal; + t = 1; + } + + if (partial) { + t = domain.valueAt(t); + } + return t; +} + //////////////////////////////////////////////////////////////////////////////// // D2<SBasis> versions /* - * Return the parameter t of a nearest point on the portion of the curve "c", + * Return the parameter t of the nearest time value on the portion of the curve "c", * related to the interval [from, to], to the point "p". * The needed curve derivative "dc" is passed as parameter. - * The function return the first nearest point to "p" that is found. + * The function return the first nearest time value to "p" that is found. */ -double nearest_point( Point const& p, - D2<SBasis> const& c, - D2<SBasis> const& dc, - double from, double to ) +double nearest_time(Point const& p, + D2<SBasis> const& c, + D2<SBasis> const& dc, + double from, double to ) { if ( from > to ) std::swap(from, to); if ( from < 0 || to > 1 ) @@ -88,21 +139,20 @@ double nearest_point( Point const& p, */ std::vector<double> -all_nearest_points( Point const& p, - D2<SBasis> const& c, - D2<SBasis> const& dc, - double from, double to ) +all_nearest_times(Point const &p, + D2<SBasis> const &c, + D2<SBasis> const &dc, + double from, double to) { - std::swap(from, to); - if ( from > to ) std::swap(from, to); - if ( from < 0 || to > 1 ) - { + if (from > to) { + std::swap(from, to); + } + if (from < 0 || to > 1) { THROW_RANGEERROR("[from,to] interval out of bounds"); } std::vector<double> result; - if (c.isConstant()) - { + if (c.isConstant()) { result.push_back(from); return result; } @@ -115,24 +165,19 @@ all_nearest_points( Point const& p, candidates.push_back(to); std::vector<double> distsq; distsq.reserve(candidates.size()); - for ( unsigned int i = 0; i < candidates.size(); ++i ) - { - distsq.push_back( L2sq(c(candidates[i]) - p) ); + for (unsigned i = 0; i < candidates.size(); ++i) { + distsq.push_back(L2sq(c(candidates[i]) - p)); } - unsigned int closest = 0; + unsigned closest = 0; double dsq = distsq[0]; - for ( unsigned int i = 1; i < candidates.size(); ++i ) - { - if ( dsq > distsq[i] ) - { + for (unsigned i = 1; i < candidates.size(); ++i) { + if (dsq > distsq[i]) { closest = i; dsq = distsq[i]; } } - for ( unsigned int i = 0; i < candidates.size(); ++i ) - { - if( distsq[closest] == distsq[i] ) - { + for (unsigned i = 0; i < candidates.size(); ++i) { + if (distsq[closest] == distsq[i]) { result.push_back(candidates[i]); } } @@ -144,39 +189,37 @@ all_nearest_points( Point const& p, // Piecewise< D2<SBasis> > versions -double nearest_point( Point const& p, - Piecewise< D2<SBasis> > const& c, - double from, double to ) +double nearest_time(Point const &p, + Piecewise< D2<SBasis> > const &c, + double from, double to) { - if ( from > to ) std::swap(from, to); - if ( from < c.cuts[0] || to > c.cuts[c.size()] ) - { + if (from > to) std::swap(from, to); + if (from < c.cuts[0] || to > c.cuts[c.size()]) { THROW_RANGEERROR("[from,to] interval out of bounds"); } - unsigned int si = c.segN(from); - unsigned int ei = c.segN(to); - if ( si == ei ) - { - double nearest= - nearest_point(p, c[si], c.segT(from, si), c.segT(to, si)); + unsigned si = c.segN(from); + unsigned ei = c.segN(to); + if (si == ei) { + double nearest = + nearest_time(p, c[si], c.segT(from, si), c.segT(to, si)); return c.mapToDomain(nearest, si); } + double t; - double nearest = nearest_point(p, c[si], c.segT(from, si)); + double nearest = nearest_time(p, c[si], c.segT(from, si)); unsigned int ni = si; double dsq; double mindistsq = distanceSq(p, c[si](nearest)); - Rect bb(Geom::Point(0,0),Geom::Point(0,0)); - for ( unsigned int i = si + 1; i < ei; ++i ) - { + Rect bb; + for (unsigned i = si + 1; i < ei; ++i) { bb = *bounds_fast(c[i]); dsq = distanceSq(p, bb); if ( mindistsq <= dsq ) continue; - t = nearest_point(p, c[i]); + + t = nearest_time(p, c[i]); dsq = distanceSq(p, c[i](t)); - if ( mindistsq > dsq ) - { + if (mindistsq > dsq) { nearest = t; ni = i; mindistsq = dsq; @@ -184,12 +227,10 @@ double nearest_point( Point const& p, } bb = *bounds_fast(c[ei]); dsq = distanceSq(p, bb); - if ( mindistsq > dsq ) - { - t = nearest_point(p, c[ei], 0, c.segT(to, ei)); + if (mindistsq > dsq) { + t = nearest_time(p, c[ei], 0, c.segT(to, ei)); dsq = distanceSq(p, c[ei](t)); - if ( mindistsq > dsq ) - { + if (mindistsq > dsq) { nearest = t; ni = ei; } @@ -198,22 +239,23 @@ double nearest_point( Point const& p, } std::vector<double> -all_nearest_points( Point const& p, - Piecewise< D2<SBasis> > const& c, - double from, double to ) +all_nearest_times(Point const &p, + Piecewise< D2<SBasis> > const &c, + double from, double to) { - if ( from > to ) std::swap(from, to); - if ( from < c.cuts[0] || to > c.cuts[c.size()] ) - { + if (from > to) { + std::swap(from, to); + } + if (from < c.cuts[0] || to > c.cuts[c.size()]) { THROW_RANGEERROR("[from,to] interval out of bounds"); } - unsigned int si = c.segN(from); - unsigned int ei = c.segN(to); + unsigned si = c.segN(from); + unsigned ei = c.segN(to); if ( si == ei ) { std::vector<double> all_nearest = - all_nearest_points(p, c[si], c.segT(from, si), c.segT(to, si)); + all_nearest_times(p, c[si], c.segT(from, si), c.segT(to, si)); for ( unsigned int i = 0; i < all_nearest.size(); ++i ) { all_nearest[i] = c.mapToDomain(all_nearest[i], si); @@ -222,18 +264,18 @@ all_nearest_points( Point const& p, } std::vector<double> all_t; std::vector< std::vector<double> > all_np; - all_np.push_back( all_nearest_points(p, c[si], c.segT(from, si)) ); - std::vector<unsigned int> ni; + all_np.push_back( all_nearest_times(p, c[si], c.segT(from, si)) ); + std::vector<unsigned> ni; ni.push_back(si); double dsq; double mindistsq = distanceSq( p, c[si](all_np.front().front()) ); - Rect bb(Geom::Point(0,0),Geom::Point(0,0)); - for ( unsigned int i = si + 1; i < ei; ++i ) - { + Rect bb; + + for (unsigned i = si + 1; i < ei; ++i) { bb = *bounds_fast(c[i]); dsq = distanceSq(p, bb); if ( mindistsq < dsq ) continue; - all_t = all_nearest_points(p, c[i]); + all_t = all_nearest_times(p, c[i]); dsq = distanceSq( p, c[i](all_t.front()) ); if ( mindistsq > dsq ) { @@ -251,29 +293,22 @@ all_nearest_points( Point const& p, } bb = *bounds_fast(c[ei]); dsq = distanceSq(p, bb); - if ( mindistsq >= dsq ) - { - all_t = all_nearest_points(p, c[ei], 0, c.segT(to, ei)); + if (mindistsq >= dsq) { + all_t = all_nearest_times(p, c[ei], 0, c.segT(to, ei)); dsq = distanceSq( p, c[ei](all_t.front()) ); - if ( mindistsq > dsq ) - { - for ( unsigned int i = 0; i < all_t.size(); ++i ) - { + if (mindistsq > dsq) { + for (unsigned int i = 0; i < all_t.size(); ++i) { all_t[i] = c.mapToDomain(all_t[i], ei); } return all_t; - } - else if ( mindistsq == dsq ) - { + } else if (mindistsq == dsq) { all_np.push_back(all_t); ni.push_back(ei); } } std::vector<double> all_nearest; - for ( unsigned int i = 0; i < all_np.size(); ++i ) - { - for ( unsigned int j = 0; j < all_np[i].size(); ++j ) - { + for (unsigned i = 0; i < all_np.size(); ++i) { + for (unsigned int j = 0; j < all_np[i].size(); ++j) { all_nearest.push_back( c.mapToDomain(all_np[i][j], ni[i]) ); } } diff --git a/src/2geom/nearest-point.h b/src/2geom/nearest-time.h index 19485242c..007cd27ba 100644 --- a/src/2geom/nearest-point.h +++ b/src/2geom/nearest-time.h @@ -1,10 +1,8 @@ -/** - * \file - * \brief nearest point routines for D2<SBasis> and Piecewise<D2<SBasis>> - * +/** @file + * @brief Nearest time routines for D2<SBasis> and Piecewise<D2<SBasis>> + *//* * Authors: - * - * Marco Cecchetti <mrcekets at gmail.com> + * Marco Cecchetti <mrcekets at gmail.com> * * Copyright 2007-2008 authors * @@ -33,8 +31,8 @@ */ -#ifndef _NEAREST_POINT_H_ -#define _NEAREST_POINT_H_ +#ifndef LIB2GEOM_SEEN_NEAREST_TIME_H +#define LIB2GEOM_SEEN_NEAREST_TIME_H #include <vector> @@ -42,7 +40,7 @@ #include <2geom/d2.h> #include <2geom/piecewise.h> #include <2geom/exception.h> - +#include <2geom/bezier.h> namespace Geom @@ -53,81 +51,91 @@ namespace Geom * return the point on L nearest to p. Note that the returned value * is with respect to the _normalized_ direction of v! */ -inline double nearest_point(Point const &p, Point const &A, Point const &v) +inline double nearest_time(Point const &p, Point const &A, Point const &v) { Point d(p - A); return d[0] * v[0] + d[1] * v[1]; } +Coord nearest_time(Point const &p, D2<Bezier> const &bez, Coord from = 0, Coord to = 1); + //////////////////////////////////////////////////////////////////////////////// // D2<SBasis> versions /* * Return the parameter t of a nearest point on the portion of the curve "c", * related to the interval [from, to], to the point "p". - * The needed curve derivative "dc" is passed as parameter. + * The needed curve derivative "deriv" is passed as parameter. * The function return the first nearest point to "p" that is found. */ -double nearest_point( Point const& p, - D2<SBasis> const& c, D2<SBasis> const& dc, - double from = 0, double to = 1 ); +double nearest_time(Point const &p, + D2<SBasis> const &c, D2<SBasis> const &deriv, + double from = 0, double to = 1); inline -double nearest_point( Point const& p, - D2<SBasis> const& c, - double from = 0, double to = 1 ) +double nearest_time(Point const &p, + D2<SBasis> const &c, + double from = 0, double to = 1 ) { - return nearest_point(p, c, Geom::derivative(c), from, to); + return nearest_time(p, c, Geom::derivative(c), from, to); } /* - * Return the parameters t of all the nearest points on the portion of + * Return the parameters t of all the nearest times on the portion of * the curve "c", related to the interval [from, to], to the point "p". * The needed curve derivative "dc" is passed as parameter. */ std::vector<double> -all_nearest_points( Point const& p, - D2<SBasis> const& c, D2<SBasis> const& dc, - double from = 0, double to = 1 ); +all_nearest_times(Point const& p, + D2<SBasis> const& c, D2<SBasis> const& dc, + double from = 0, double to = 1 ); inline std::vector<double> -all_nearest_points( Point const& p, - D2<SBasis> const& c, - double from = 0, double to = 1 ) +all_nearest_times(Point const &p, + D2<SBasis> const &c, + double from = 0, double to = 1) { - return all_nearest_points(p, c, Geom::derivative(c), from, to); + return all_nearest_times(p, c, Geom::derivative(c), from, to); } //////////////////////////////////////////////////////////////////////////////// // Piecewise< D2<SBasis> > versions -double nearest_point( Point const& p, - Piecewise< D2<SBasis> > const& c, - double from, double to ); +double nearest_time(Point const &p, + Piecewise< D2<SBasis> > const &c, + double from, double to); inline -double nearest_point( Point const& p, Piecewise< D2<SBasis> > const& c ) +double nearest_time(Point const& p, Piecewise< D2<SBasis> > const &c) { - return nearest_point(p, c, c.cuts[0], c.cuts[c.size()]); + return nearest_time(p, c, c.cuts[0], c.cuts[c.size()]); } std::vector<double> -all_nearest_points( Point const& p, - Piecewise< D2<SBasis> > const& c, - double from, double to ); +all_nearest_times(Point const &p, + Piecewise< D2<SBasis> > const &c, + double from, double to); inline std::vector<double> -all_nearest_points( Point const& p, Piecewise< D2<SBasis> > const& c ) +all_nearest_times( Point const& p, Piecewise< D2<SBasis> > const& c ) { - return all_nearest_points(p, c, c.cuts[0], c.cuts[c.size()]); + return all_nearest_times(p, c, c.cuts[0], c.cuts[c.size()]); } } // end namespace Geom - - -#endif /*_NEAREST_POINT_H_*/ +#endif // LIB2GEOM_SEEN_NEAREST_TIME_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/2geom/numeric/fitting-model.h b/src/2geom/numeric/fitting-model.h index a44c1ddac..fb96d1d2a 100644 --- a/src/2geom/numeric/fitting-model.h +++ b/src/2geom/numeric/fitting-model.h @@ -39,7 +39,7 @@ #include <2geom/sbasis.h> #include <2geom/bezier.h> #include <2geom/bezier-curve.h> -#include <2geom/poly.h> +#include <2geom/polynomial.h> #include <2geom/ellipse.h> #include <2geom/circle.h> #include <2geom/utils.h> @@ -298,7 +298,7 @@ class LFMEllipse public: void instance(Ellipse & e, ConstVectorView const& coeff) const { - e.set(1, coeff[0], coeff[1], coeff[2], coeff[3], coeff[4]); + e.setCoefficients(1, coeff[0], coeff[1], coeff[2], coeff[3], coeff[4]); } }; @@ -333,7 +333,7 @@ class LFMCircle public: void instance(Circle & c, ConstVectorView const& coeff) const { - c.set(1, coeff[0], coeff[1], coeff[2]); + c.setCoefficients(1, coeff[0], coeff[1], coeff[2]); } }; diff --git a/src/2geom/numeric/matrix.h b/src/2geom/numeric/matrix.h index a130bd748..ddca35cd6 100644 --- a/src/2geom/numeric/matrix.h +++ b/src/2geom/numeric/matrix.h @@ -432,16 +432,17 @@ class Matrix: public detail::MatrixImpl inline void swap(Matrix & m1, Matrix & m2) { - assert( m1.rows() == m2.rows() && m1.columns() == m2.columns() ); - std::swap(m1.m_matrix, m2.m_matrix); + assert(m1.rows() == m2.rows() && m1.columns() == m2.columns()); + using std::swap; + swap(m1.m_matrix, m2.m_matrix); } -inline -void swap_any(Matrix & m1, Matrix & m2) +inline void swap_any(Matrix &m1, Matrix &m2) { - std::swap(m1.m_matrix, m2.m_matrix); - std::swap(m1.m_rows, m2.m_rows); - std::swap(m1.m_columns, m2.m_columns); + using std::swap; + swap(m1.m_matrix, m2.m_matrix); + swap(m1.m_rows, m2.m_rows); + swap(m1.m_columns, m2.m_columns); } @@ -569,8 +570,9 @@ class MatrixView : public detail::MatrixImpl inline void swap_view(MatrixView & m1, MatrixView & m2) { - assert( m1.rows() == m2.rows() && m1.columns() == m2.columns() ); - std::swap(m1.m_matrix_view, m2.m_matrix_view); + assert(m1.rows() == m2.rows() && m1.columns() == m2.columns()); + using std::swap; + swap(m1.m_matrix_view, m2.m_matrix_view); } Vector operator*( detail::BaseMatrixImpl const& A, diff --git a/src/2geom/numeric/symmetric-matrix-fs-operation.h b/src/2geom/numeric/symmetric-matrix-fs-operation.h index 37ece56ae..c5aaa724b 100644 --- a/src/2geom/numeric/symmetric-matrix-fs-operation.h +++ b/src/2geom/numeric/symmetric-matrix-fs-operation.h @@ -43,12 +43,7 @@ namespace Geom { namespace NL { template <size_t N> -inline -SymmetricMatrix<N> adj(const ConstBaseSymmetricMatrix<N> & /*S*/) -{ - THROW_NOTIMPLEMENTED(); - return SymmetricMatrix<N>(); -} +SymmetricMatrix<N> adj(const ConstBaseSymmetricMatrix<N> & S); template <> inline diff --git a/src/2geom/numeric/symmetric-matrix-fs-trace.h b/src/2geom/numeric/symmetric-matrix-fs-trace.h index dbabecf6e..eff3dd24d 100644 --- a/src/2geom/numeric/symmetric-matrix-fs-trace.h +++ b/src/2geom/numeric/symmetric-matrix-fs-trace.h @@ -73,13 +73,7 @@ bool abs_less (double x, double y) template <size_t K, size_t N> struct trace { - static - double evaluate (const ConstBaseSymmetricMatrix<N> & /*S*/) - { - THROW_NOTIMPLEMENTED(); - return K; - } - + static double evaluate(const ConstBaseSymmetricMatrix<N> &S); }; template <size_t N> diff --git a/src/2geom/numeric/symmetric-matrix-fs.h b/src/2geom/numeric/symmetric-matrix-fs.h index c1de27afd..2fadd6915 100644 --- a/src/2geom/numeric/symmetric-matrix-fs.h +++ b/src/2geom/numeric/symmetric-matrix-fs.h @@ -339,7 +339,10 @@ class BaseSymmetricMatrix : public ConstBaseSymmetricMatrix<N> { } - using base_type::operator(); + double operator() (size_t i, size_t j) const + { + return m_data[base_type::get_index(i,j)]; + } double& operator() (size_t i, size_t j) { diff --git a/src/2geom/numeric/vector.h b/src/2geom/numeric/vector.h index 6ab898f29..f28289f0f 100644 --- a/src/2geom/numeric/vector.h +++ b/src/2geom/numeric/vector.h @@ -348,15 +348,17 @@ class Vector : public detail::VectorImpl inline void swap(Vector & v1, Vector & v2) { - assert( v1.size() == v2.size() ); - std::swap(v1.m_vector, v2.m_vector); + assert(v1.size() == v2.size()); + using std::swap; + swap(v1.m_vector, v2.m_vector); } inline void swap_any(Vector & v1, Vector & v2) { - std::swap(v1.m_vector, v2.m_vector); - std::swap(v1.m_size, v2.m_size); + using std::swap; + swap(v1.m_vector, v2.m_vector); + swap(v1.m_size, v2.m_size); } @@ -552,7 +554,8 @@ inline void swap_view(VectorView & v1, VectorView & v2) { assert( v1.size() == v2.size() ); - std::swap(v1.m_vector_view, v2.m_vector_view); // not swap m_vector too + using std::swap; + swap(v1.m_vector_view, v2.m_vector_view); // not swap m_vector too } inline diff --git a/src/2geom/ord.h b/src/2geom/ord.h index 0add83da4..e190a4a1e 100644 --- a/src/2geom/ord.h +++ b/src/2geom/ord.h @@ -1,6 +1,5 @@ -/** - * \file - * \brief Comparator template +/** @file + * @brief Comparator template *//* * Authors: * ? <?@?.?> @@ -32,8 +31,8 @@ * */ -#ifndef __2GEOM_ORD__ -#define __2GEOM_ORD__ +#ifndef LIB2GEOM_SEEN_ORD_H +#define LIB2GEOM_SEEN_ORD_H namespace { diff --git a/src/2geom/path-intersection.cpp b/src/2geom/path-intersection.cpp index 63a29423d..07e38ba9e 100644 --- a/src/2geom/path-intersection.cpp +++ b/src/2geom/path-intersection.cpp @@ -12,98 +12,9 @@ namespace Geom { -/** - * This function computes the winding of the path, given a reference point. - * Positive values correspond to counter-clockwise in the mathematical coordinate system, - * and clockwise in screen coordinates. This particular implementation casts a ray in - * the positive x direction. It iterates the path, checking for intersection with the - * bounding boxes. If an intersection is found, the initial/final Y value of the curve is - * used to derive a delta on the winding value. If the point is within the bounding box, - * the curve specific winding function is called. - */ -int winding(Path const &path, Point p) { - //start on a segment which is not a horizontal line with y = p[y] - Path::const_iterator start; - for(Path::const_iterator iter = path.begin(); ; ++iter) { - if(iter == path.end_closed()) { return 0; } - if(iter->initialPoint()[Y]!=p[Y]) { start = iter; break; } - if(iter->finalPoint()[Y]!=p[Y]) { start = iter; break; } - if(iter->boundsFast().height()!=0.){ start = iter; break; } - } - int wind = 0; - unsigned cnt = 0; - bool starting = true; - for (Path::const_iterator iter = start; iter != start || starting - ; ++iter, iter = (iter == path.end_closed()) ? path.begin() : iter ) - { - cnt++; - if(cnt > path.size()) return wind; //some bug makes this required - starting = false; - Rect bounds = (iter->boundsFast()); - Coord x = p[X], y = p[Y]; - - if(x > bounds.right() || !bounds[Y].contains(y)) continue; //ray doesn't intersect box - - Point final = iter->finalPoint(); - Point initial = iter->initialPoint(); - Cmp final_to_ray = cmp(final[Y], y); - Cmp initial_to_ray = cmp(initial[Y], y); - - // if y is included, these will have opposite values, giving order. - Cmp c = cmp(final_to_ray, initial_to_ray); - if(x < bounds.left()) { - // ray goes through bbox - // winding delta determined by position of endpoints - if(final_to_ray != EQUAL_TO) { - wind += int(c); // GT = counter-clockwise = 1; LT = clockwise = -1; EQ = not-included = 0 - //std::cout << int(c) << " "; - goto cont; - } - } else { - //inside bbox, use custom per-curve winding thingie - int delt = iter->winding(p); - wind += delt; - //std::cout << "n" << delt << " "; - } - //Handling the special case of an endpoint on the ray: - if(final[Y] == y) { - //Traverse segments until it breaks away from y - //99.9% of the time this will happen the first go - Path::const_iterator next = iter; - ++next; - for(; ; ++next) { - if(next == path.end_closed()) next = path.begin(); - Rect bnds = (next->boundsFast()); - //TODO: X considerations - if(bnds.height() > 0) { - //It has diverged - if(bnds.contains(p)) { - const double fudge = 0.01; - if(cmp(y, next->valueAt(fudge, Y)) == initial_to_ray) { - wind += int(c); - //std::cout << "!!!!!" << int(c) << " "; - } - iter = next; // No increment, as the rest of the thing hasn't been counted. - } else { - Coord ny = next->initialPoint()[Y]; - if(cmp(y, ny) == initial_to_ray) { - //Is a continuation through the ray, so counts windingwise - wind += int(c); - //std::cout << "!!!!!" << int(c) << " "; - } - iter = ++next; - } - goto cont; - } - if(next==start) return wind; - } - //Looks like it looped, which means everything's flat - return 0; - } - - cont:(void)0; - } - return wind; +/// Compute winding number of the path at the specified point +int winding(Path const &path, Point const &p) { + return path.winding(p); } /** @@ -162,7 +73,7 @@ void append(T &a, T const &b) { * indicates if the time values are within their proper range on the line segments. */ bool -linear_intersect(Point A0, Point A1, Point B0, Point B1, +linear_intersect(Point const &A0, Point const &A1, Point const &B0, Point const &B1, double &tA, double &tB, double &det) { bool both_lines_non_zero = (!are_near(A0, A1)) && (!are_near(B0, B1)); @@ -521,7 +432,7 @@ std::vector<double> path_mono_splits(Path const &p) { * Applies path_mono_splits to multiple paths, and returns the results such that * time-set i corresponds to Path i. */ -std::vector<std::vector<double> > paths_mono_splits(std::vector<Path> const &ps) { +std::vector<std::vector<double> > paths_mono_splits(PathVector const &ps) { std::vector<std::vector<double> > ret; for(unsigned i = 0; i < ps.size(); i++) ret.push_back(path_mono_splits(ps[i])); @@ -533,7 +444,7 @@ std::vector<std::vector<double> > paths_mono_splits(std::vector<Path> const &ps) * Each entry i corresponds to path i of the input. The number of rects in each entry is guaranteed to be the * number of splits for that path, subtracted by one. */ -std::vector<std::vector<Rect> > split_bounds(std::vector<Path> const &p, std::vector<std::vector<double> > splits) { +std::vector<std::vector<Rect> > split_bounds(PathVector const &p, std::vector<std::vector<double> > splits) { std::vector<std::vector<Rect> > ret; for(unsigned i = 0; i < p.size(); i++) { std::vector<Rect> res; @@ -553,7 +464,7 @@ std::vector<std::vector<Rect> > split_bounds(std::vector<Path> const &p, std::ve * This function does two sweeps, one on the bounds of each path, and after that cull, one on the curves within. * This leads to a certain amount of code complexity, however, most of that is factored into the above functions */ -CrossingSet MonoCrosser::crossings(std::vector<Path> const &a, std::vector<Path> const &b) { +CrossingSet MonoCrosser::crossings(PathVector const &a, PathVector const &b) { if(b.empty()) return CrossingSet(a.size(), Crossings()); CrossingSet results(a.size() + b.size(), Crossings()); if(a.empty()) return results; @@ -596,7 +507,7 @@ CrossingSet MonoCrosser::crossings(std::vector<Path> const &a, std::vector<Path> /* This function is similar codewise to the MonoCrosser, the main difference is that it deals with * only one set of paths and includes self intersection -CrossingSet crossings_among(std::vector<Path> const &p) { +CrossingSet crossings_among(PathVector const &p) { CrossingSet results(p.size(), Crossings()); if(p.empty()) return results; @@ -780,7 +691,7 @@ void flip_crossings(Crossings &crs) { crs[i] = Crossing(crs[i].tb, crs[i].ta, crs[i].b, crs[i].a, !crs[i].dir); } -CrossingSet crossings_among(std::vector<Path> const &p) { +CrossingSet crossings_among(PathVector const &p) { CrossingSet results(p.size(), Crossings()); if(p.empty()) return results; diff --git a/src/2geom/path-intersection.h b/src/2geom/path-intersection.h index 512c31167..f06eeaf94 100644 --- a/src/2geom/path-intersection.h +++ b/src/2geom/path-intersection.h @@ -32,21 +32,19 @@ * */ -#ifndef __GEOM_PATH_INTERSECTION_H -#define __GEOM_PATH_INTERSECTION_H - -#include <2geom/path.h> +#ifndef LIB2GEOM_SEEN_PATH_INTERSECTION_H +#define LIB2GEOM_SEEN_PATH_INTERSECTION_H #include <2geom/crossing.h> - -#include <2geom/sweep.h> +#include <2geom/path.h> +#include <2geom/sweep-bounds.h> namespace Geom { -int winding(Path const &path, Point p); +int winding(Path const &path, Point const &p); bool path_direction(Path const &p); -inline bool contains(Path const & p, Point i, bool evenodd = true) { +inline bool contains(Path const & p, Point const &i, bool evenodd = true) { return (evenodd ? winding(p, i) % 2 : winding(p, i)) != 0; } @@ -74,19 +72,19 @@ Crossings mono_intersect(Curve const & A, Interval const &Ad, struct SimpleCrosser : public Crosser<Path> { Crossings crossings(Curve const &a, Curve const &b); Crossings crossings(Path const &a, Path const &b) { return curve_sweep<SimpleCrosser>(a, b); } - CrossingSet crossings(std::vector<Path> const &a, std::vector<Path> const &b) { return Crosser<Path>::crossings(a, b); } + CrossingSet crossings(PathVector const &a, PathVector const &b) { return Crosser<Path>::crossings(a, b); } }; struct MonoCrosser : public Crosser<Path> { - Crossings crossings(Path const &a, Path const &b) { return crossings(std::vector<Path>(1,a), std::vector<Path>(1,b))[0]; } - CrossingSet crossings(std::vector<Path> const &a, std::vector<Path> const &b); + Crossings crossings(Path const &a, Path const &b) { return crossings(PathVector(a), PathVector(b))[0]; } + CrossingSet crossings(PathVector const &a, PathVector const &b); }; typedef SimpleCrosser DefaultCrosser; std::vector<double> path_mono_splits(Path const &p); -CrossingSet crossings_among(std::vector<Path> const & p); +CrossingSet crossings_among(PathVector const & p); Crossings self_crossings(Path const & a); inline Crossings crossings(Curve const & a, Curve const & b) { @@ -99,7 +97,7 @@ inline Crossings crossings(Path const & a, Path const & b) { return c.crossings(a, b); } -inline CrossingSet crossings(std::vector<Path> const & a, std::vector<Path> const & b) { +inline CrossingSet crossings(PathVector const & a, PathVector const & b) { DefaultCrosser c = DefaultCrosser(); return c.crossings(a, b); } diff --git a/src/2geom/path-sink.cpp b/src/2geom/path-sink.cpp index 6acd9508c..3b8d407f8 100644 --- a/src/2geom/path-sink.cpp +++ b/src/2geom/path-sink.cpp @@ -31,80 +31,65 @@ #include <2geom/sbasis-to-bezier.h> #include <2geom/path-sink.h> #include <2geom/exception.h> +#include <2geom/circle.h> +#include <2geom/ellipse.h> namespace Geom { -void output(Curve const &curve, PathSink &sink) { - std::vector<Point> pts; - sbasis_to_bezier(pts, curve.toSBasis(), 2); //TODO: use something better! - sink.curveTo(pts[0], pts[1], pts[2]); +void PathSink::feed(Curve const &c, bool moveto_initial) +{ + c.feed(*this, moveto_initial); } -void output(HLineSegment const &curve, PathSink &sink) { - sink.hlineTo(curve.finalPoint()[X]); -} - -void output(VLineSegment const &curve, PathSink &sink) { - sink.vlineTo(curve.finalPoint()[Y]); -} - -void output(LineSegment const &curve, PathSink &sink) { - sink.lineTo(curve[1]); -} - -void output(CubicBezier const &curve, PathSink &sink) { - sink.curveTo(curve[1], curve[2], curve[3]); -} - -void output(QuadraticBezier const &curve, PathSink &sink) { - sink.quadTo(curve[1], curve[2]); -} - -void output(SVGEllipticalArc const &curve, PathSink &sink) { - sink.arcTo( curve.ray(X), curve.ray(Y), curve.rotationAngle(), - curve.largeArc(), curve.sweep(), - curve.finalPoint() ); -} - -template <typename T> -bool output_as(Curve const &curve, PathSink &sink) { - T const *t = dynamic_cast<T const *>(&curve); - if (t) { - output(*t, sink); - return true; - } else { - return false; - } -} - -void PathSink::path(Path const &path) { +void PathSink::feed(Path const &path) { flush(); moveTo(path.front().initialPoint()); - Path::const_iterator iter; - for (iter = path.begin(); iter != path.end(); ++iter) { - output_as<HLineSegment>(*iter, *this) || - output_as<VLineSegment>(*iter, *this) || - output_as<LineSegment>(*iter, *this) || - output_as<CubicBezier>(*iter, *this) || - output_as<QuadraticBezier>(*iter, *this) || - output_as<SVGEllipticalArc>(*iter, *this) || - output_as<Curve>(*iter, *this); + // never output the closing segment to the sink + Path::const_iterator iter = path.begin(), last = path.end_open(); + for (; iter != last; ++iter) { + iter->feed(*this, false); } - if (path.closed()) { closePath(); } flush(); } -void PathSink::pathvector(PathVector const &pv) { - flush(); +void PathSink::feed(PathVector const &pv) { for (PathVector::const_iterator i = pv.begin(); i != pv.end(); ++i) { - path(*i); + feed(*i); } } +void PathSink::feed(Rect const &r) { + moveTo(r.corner(0)); + lineTo(r.corner(1)); + lineTo(r.corner(2)); + lineTo(r.corner(3)); + closePath(); +} + +void PathSink::feed(Circle const &e) { + Coord r = e.radius(); + Point c = e.center(); + Point a = c + Point(0, c[Y] + r); + Point b = c + Point(0, c[Y] - r); + + moveTo(a); + arcTo(r, r, 0, false, false, b); + arcTo(r, r, 0, false, false, a); + closePath(); +} + +void PathSink::feed(Ellipse const &e) { + Point s = e.pointAt(0); + moveTo(s); + arcTo(e.ray(X), e.ray(Y), e.rotationAngle(), false, false, e.pointAt(M_PI)); + arcTo(e.ray(X), e.ray(Y), e.rotationAngle(), false, false, s); + closePath(); +} + } /* diff --git a/src/2geom/path-sink.h b/src/2geom/path-sink.h index 949369b80..17ede18a4 100644 --- a/src/2geom/path-sink.h +++ b/src/2geom/path-sink.h @@ -29,9 +29,10 @@ * */ -#ifndef SEEN_SVG_PATH_H -#define SEEN_SVG_PATH_H +#ifndef LIB2GEOM_SEEN_PATH_SINK_H +#define LIB2GEOM_SEEN_PATH_SINK_H +#include <2geom/forward.h> #include <2geom/pathvector.h> #include <2geom/curves.h> #include <iterator> @@ -44,58 +45,73 @@ namespace Geom { * PathSink provides an interface that allows one to easily write * code which processes path data, for instance when converting * between path formats used by different graphics libraries. + * It is also useful for writing algorithms which must do something + * for each curve in the path. * * To store a path in a new format, implement the virtual methods - * for segments in a derived class and call path() or pathvector(). + * for segments in a derived class and call feed(). + * + * @ingroup Paths */ class PathSink { public: - /** Move to a different point without creating a segment. + /** @brief Move to a different point without creating a segment. * Usually starts a new subpath. */ virtual void moveTo(Point const &p) = 0; - /// Output a horizontal line segment. Only the X coordinate of the final point is given. - virtual void hlineTo(Coord v) = 0; - /// Output a vertical line segment. Only the Y coordinate of the final point is given. - virtual void vlineTo(Coord v) = 0; /// Output a line segment. virtual void lineTo(Point const &p) = 0; /// Output a quadratic Bezier segment. virtual void curveTo(Point const &c0, Point const &c1, Point const &p) = 0; /// Output a cubic Bezier segment. virtual void quadTo(Point const &c, Point const &p) = 0; - /** Output an elliptical arc segment. + /** @brief Output an elliptical arc segment. * See the EllipticalArc class for the documentation of parameters. */ - virtual void arcTo(double rx, double ry, double angle, + virtual void arcTo(Coord rx, Coord ry, Coord angle, bool large_arc, bool sweep, Point const &p) = 0; /// Close the current path with a line segment. virtual void closePath() = 0; - /** Flush any internal state of the generator. - * + /** @brief Flush any internal state of the generator. * This call should implicitly finish the current subpath. * Calling this method should be idempotent, because the default - * implementations of path() and pathvector() will be call it + * implementations of path() and pathvector() will call it * multiple times in a row. */ virtual void flush() = 0; + // Get the current point, e.g. where the initial point of the next segment will be. + //virtual Point currentPoint() const = 0; - /** Undo the last segment. + /** @brief Undo the last segment. * This method is optional. * @return true true if a segment was erased, false otherwise. */ virtual bool backspace() { return false; } // these have a default implementation - /** Output a subpath. - * Calls the appropriate segment methods according to the contents - * of the passed subpath. You can override this function. */ - virtual void path(Path const &p); - /** Output a path. + virtual void feed(Curve const &c, bool moveto_initial = true); + /** @brief Output a subpath. * Calls the appropriate segment methods according to the contents - * of the passed path. You can override this function. */ - virtual void pathvector(PathVector const &v); + * of the passed subpath. You can override this function. + * NOTE: if you override only some of the feed() functions, + * always write this in the derived class: + * @code + using PathSink::feed; + @endcode + * Otherwise the remaining methods will be hidden. */ + virtual void feed(Path const &p); + /** @brief Output a path. + * Calls feed() on each path in the vector. You can override this function. */ + virtual void feed(PathVector const &v); + /// Output an axis-aligned rectangle, using moveTo, lineTo and closePath. + virtual void feed(Rect const &); + /// Output a circle as two elliptical arcs. + virtual void feed(Circle const &e); + /// Output an ellipse as two elliptical arcs. + virtual void feed(Ellipse const &e); virtual ~PathSink() {} }; +/** @brief Store paths to an output iterator + * @ingroup Paths */ template <typename OutputIterator> class PathIteratorSink : public PathSink { public: @@ -110,22 +126,6 @@ public: } //TODO: what if _in_path = false? - void hlineTo(Coord v) { - // check for implicit moveto, like in: "M 1,1 L 2,2 z l 2,2 z" - if (!_in_path) { - moveTo(_start_p); - } - _path.template appendNew<HLineSegment>(Point(v, _path.finalPoint()[Y])); - } - - void vlineTo(Coord v) { - // check for implicit moveto, like in: "M 1,1 L 2,2 z l 2,2 z" - if (!_in_path) { - moveTo(_start_p); - } - _path.template appendNew<VLineSegment>(Point(_path.finalPoint()[X], v)); - } - void lineTo(Point const &p) { // check for implicit moveto, like in: "M 1,1 L 2,2 z l 2,2 z" if (!_in_path) { @@ -150,15 +150,15 @@ public: _path.template appendNew<CubicBezier>(c0, c1, p); } - void arcTo(double rx, double ry, double angle, + void arcTo(Coord rx, Coord ry, Coord angle, bool large_arc, bool sweep, Point const &p) { // check for implicit moveto, like in: "M 1,1 L 2,2 z l 2,2 z" if (!_in_path) { moveTo(_start_p); } - _path.template appendNew<SVGEllipticalArc>(rx, ry, angle, - large_arc, sweep, p); + _path.template appendNew<EllipticalArc>(rx, ry, angle, + large_arc, sweep, p); } bool backspace() @@ -170,12 +170,12 @@ public: return false; } - void append(Path const &other, Path::Stitching stitching = Path::NO_STITCHING) + void append(Path const &other) { if (!_in_path) { moveTo(other.initialPoint()); } - _path.append(other, stitching); + _path.append(other); } void closePath() { @@ -188,11 +188,15 @@ public: _in_path = false; *_out++ = _path; _path.clear(); - _path.close(false); } } + + void setStitching(bool s) { + _path.setStitching(s); + } - void path(Path const &other) + using PathSink::feed; + void feed(Path const &other) { flush(); *_out++ = other; @@ -205,14 +209,23 @@ protected: Point _start_p; }; -typedef std::back_insert_iterator<std::vector<Path> > iter; +typedef std::back_insert_iterator<PathVector> SubpathInserter; -class PathBuilder : public PathIteratorSink<iter> { +/** @brief Store paths to a PathVector + * @ingroup Paths */ +class PathBuilder : public PathIteratorSink<SubpathInserter> { private: - std::vector<Path> _pathset; + PathVector _pathset; public: - PathBuilder() : PathIteratorSink<iter>(iter(_pathset)) {} - std::vector<Path> const &peek() const {return _pathset;} + /// Create a builder that outputs to an internal pathvector. + PathBuilder() : PathIteratorSink<SubpathInserter>(SubpathInserter(_pathset)) {} + /// Create a builder that outputs to pathvector given by reference. + PathBuilder(PathVector &pv) : PathIteratorSink<SubpathInserter>(SubpathInserter(pv)) {} + + /// Retrieve the path + PathVector const &peek() const {return _pathset;} + /// Clear the stored path vector + void clear() { _pathset.clear(); } }; } diff --git a/src/2geom/path.cpp b/src/2geom/path.cpp index 3558af3b3..871441751 100644 --- a/src/2geom/path.cpp +++ b/src/2geom/path.cpp @@ -1,11 +1,12 @@ -/* - * Path - Series of continuous curves - * +/** @file + * @brief Path - a sequence of contiguous curves (implementation file) + *//* * Authors: - * MenTaLguY <mental@rydia.net> - * Marco Cecchetti <mrcekets at gmail.com> + * MenTaLguY <mental@rydia.net> + * Marco Cecchetti <mrcekets at gmail.com> + * Krzysztof Kosiński <tweenk.pl@gmail.com> * - * Copyright 2007-2008 authors + * Copyright 2007-2014 authors * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public @@ -31,414 +32,1020 @@ * the specific language governing rights and limitations. */ - - - #include <2geom/path.h> +#include <2geom/pathvector.h> #include <2geom/transforms.h> +#include <2geom/circle.h> +#include <2geom/ellipse.h> +#include <2geom/convex-hull.h> +#include <2geom/svg-path-writer.h> +#include <2geom/sweeper.h> #include <algorithm> +#include <limits> using std::swap; using namespace Geom::PathInternal; -namespace Geom +namespace Geom { + +// this represents an empty interval +PathInterval::PathInterval() + : _from(0, 0.0) + , _to(0, 0.0) + , _path_size(1) + , _cross_start(false) + , _reverse(false) +{} + +PathInterval::PathInterval(PathTime const &from, PathTime const &to, bool cross_start, size_type path_size) + : _from(from) + , _to(to) + , _path_size(path_size) + , _cross_start(cross_start) + , _reverse(cross_start ? to >= from : to < from) +{ + if (_reverse) { + _to.normalizeForward(_path_size); + if (_from != _to) { + _from.normalizeBackward(_path_size); + } + } else { + _from.normalizeForward(_path_size); + if (_from != _to) { + _to.normalizeBackward(_path_size); + } + } + + if (_from == _to) { + _reverse = false; + _cross_start = false; + } +} + +bool PathInterval::contains(PathTime const &pos) const { + if (_cross_start) { + if (_reverse) { + return pos >= _to || _from >= pos; + } else { + return pos >= _from || _to >= pos; + } + } else { + if (_reverse) { + return _to <= pos && pos <= _from; + } else { + return _from <= pos && pos <= _to; + } + } +} + +PathTime PathInterval::inside(Coord min_dist) const +{ + // If there is some node further than min_dist (in time coord) from the ends, + // return that node. Otherwise, return the middle. + PathTime result(0, 0.0); + + if (!_cross_start && _from.curve_index == _to.curve_index) { + PathTime result(_from.curve_index, lerp(0.5, _from.t, _to.t)); + return result; + } + // If _cross_start, then we can be sure that at least one node is in the domain. + // If dcurve == 0, it actually means that all curves are included in the domain + + if (_reverse) { + size_type dcurve = (_path_size + _from.curve_index - _to.curve_index) % _path_size; + bool from_close = _from.t < min_dist; + bool to_close = _to.t > 1 - min_dist; + + if (dcurve == 0) { + dcurve = _path_size; + } + + if (dcurve == 1) { + if (from_close || to_close) { + result.curve_index = _from.curve_index; + Coord tmid = _from.t - ((1 - _to.t) + _from.t) * 0.5; + if (tmid < 0) { + result.curve_index = (_path_size + result.curve_index - 1) % _path_size; + tmid += 1; + } + result.t = tmid; + return result; + } + + result.curve_index = _from.curve_index; + return result; + } + + result.curve_index = (_to.curve_index + 1) % _path_size; + if (to_close) { + if (dcurve == 2) { + result.t = 0.5; + } else { + result.curve_index = (result.curve_index + 1) % _path_size; + } + } + return result; + } else { + size_type dcurve = (_path_size + _to.curve_index - _from.curve_index) % _path_size; + bool from_close = _from.t > 1 - min_dist; + bool to_close = _to.t < min_dist; + + if (dcurve == 0) { + dcurve = _path_size; + } + + if (dcurve == 1) { + if (from_close || to_close) { + result.curve_index = _from.curve_index; + Coord tmid = ((1 - _from.t) + _to.t) * 0.5 + _from.t; + if (tmid >= 1) { + result.curve_index = (result.curve_index + 1) % _path_size; + tmid -= 1; + } + result.t = tmid; + return result; + } + + result.curve_index = _to.curve_index; + return result; + } + + result.curve_index = (_from.curve_index + 1) % _path_size; + if (from_close) { + if (dcurve == 2) { + result.t = 0.5; + } else { + result.curve_index = (result.curve_index + 1) % _path_size; + } + } + return result; + } + + result.curve_index = _reverse ? _from.curve_index : _to.curve_index; + return result; +} + +PathInterval PathInterval::from_direction(PathTime const &from, PathTime const &to, bool reversed, size_type path_size) +{ + PathInterval result; + result._from = from; + result._to = to; + result._path_size = path_size; + + if (reversed) { + result._to.normalizeForward(path_size); + if (result._from != result._to) { + result._from.normalizeBackward(path_size); + } + } else { + result._from.normalizeForward(path_size); + if (result._from != result._to) { + result._to.normalizeBackward(path_size); + } + } + + if (result._from == result._to) { + result._reverse = false; + result._cross_start = false; + } else { + result._reverse = reversed; + if (reversed) { + result._cross_start = from < to; + } else { + result._cross_start = to < from; + } + } + return result; +} + + +Path::Path(Rect const &r) + : _curves(new Sequence()) + , _closing_seg(new ClosingSegment(r.corner(3), r.corner(0))) + , _closed(true) + , _exception_on_stitch(true) +{ + for (unsigned i = 0; i < 3; ++i) { + _curves->push_back(new LineSegment(r.corner(i), r.corner(i+1))); + } + _curves->push_back(_closing_seg); +} + +Path::Path(ConvexHull const &ch) + : _curves(new Sequence()) + , _closing_seg(new ClosingSegment(Point(), Point())) + , _closed(true) + , _exception_on_stitch(true) +{ + if (ch.empty()) { + _curves->push_back(_closing_seg); + return; + } + + _closing_seg->setInitial(ch.back()); + _closing_seg->setFinal(ch.front()); + + Point last = ch.front(); + + for (std::size_t i = 1; i < ch.size(); ++i) { + _curves->push_back(new LineSegment(last, ch[i])); + last = ch[i]; + } + + _curves->push_back(_closing_seg); + _closed = true; +} + +Path::Path(Circle const &c) + : _curves(new Sequence()) + , _closing_seg(NULL) + , _closed(true) + , _exception_on_stitch(true) +{ + Point p1 = c.pointAt(0); + Point p2 = c.pointAt(M_PI); + _curves->push_back(new EllipticalArc(p1, c.radius(), c.radius(), 0, false, true, p2)); + _curves->push_back(new EllipticalArc(p2, c.radius(), c.radius(), 0, false, true, p1)); + _closing_seg = new ClosingSegment(p1, p1); + _curves->push_back(_closing_seg); +} + +Path::Path(Ellipse const &e) + : _curves(new Sequence()) + , _closing_seg(NULL) + , _closed(true) + , _exception_on_stitch(true) +{ + Point p1 = e.pointAt(0); + Point p2 = e.pointAt(M_PI); + _curves->push_back(new EllipticalArc(p1, e.rays(), e.rotationAngle(), false, true, p2)); + _curves->push_back(new EllipticalArc(p2, e.rays(), e.rotationAngle(), false, true, p1)); + _closing_seg = new ClosingSegment(p1, p1); + _curves->push_back(_closing_seg); +} + +void Path::close(bool c) +{ + if (c == _closed) return; + if (c && _curves->size() >= 2) { + // when closing, if last segment is linear and ends at initial point, + // replace it with the closing segment + Sequence::iterator last = _curves->end() - 2; + if (last->isLineSegment() && last->finalPoint() == initialPoint()) { + _closing_seg->setInitial(last->initialPoint()); + _curves->erase(last); + } + } + _closed = c; +} + +void Path::clear() { + _unshare(); + _curves->pop_back().release(); + _curves->clear(); + _closing_seg->setInitial(Point(0, 0)); + _closing_seg->setFinal(Point(0, 0)); + _curves->push_back(_closing_seg); + _closed = false; +} -OptRect Path::boundsFast() const { - OptRect bounds; - if (empty()) return bounds; - bounds = front().boundsFast(); - const_iterator iter = begin(); - // the closing path segment can be ignored, because it will always lie within the bbox of the rest of the path - if ( iter != end() ) { - for ( ++iter; iter != end() ; ++iter ) { - bounds.unionWith(iter->boundsFast()); +OptRect Path::boundsFast() const +{ + OptRect bounds; + if (empty()) + return bounds; + bounds = front().boundsFast(); + const_iterator iter = begin(); + // the closing path segment can be ignored, because it will always lie within the bbox of the rest of the path + if (iter != end()) { + for (++iter; iter != end(); ++iter) { + bounds.unionWith(iter->boundsFast()); + } } - } - return bounds; + return bounds; } -OptRect Path::boundsExact() const { - OptRect bounds; - if (empty()) return bounds; - bounds = front().boundsExact(); - const_iterator iter = begin(); - // the closing path segment can be ignored, because it will always lie within the bbox of the rest of the path - if ( iter != end() ) { - for ( ++iter; iter != end() ; ++iter ) { - bounds.unionWith(iter->boundsExact()); +OptRect Path::boundsExact() const +{ + OptRect bounds; + if (empty()) + return bounds; + bounds = front().boundsExact(); + const_iterator iter = begin(); + // the closing path segment can be ignored, because it will always lie within the bbox of the rest of the path + if (iter != end()) { + for (++iter; iter != end(); ++iter) { + bounds.unionWith(iter->boundsExact()); + } } - } - return bounds; + return bounds; } -template<typename iter> +Piecewise<D2<SBasis> > Path::toPwSb() const +{ + Piecewise<D2<SBasis> > ret; + ret.push_cut(0); + unsigned i = 1; + bool degenerate = true; + // pw<d2<>> is always open. so if path is closed, add closing segment as well to pwd2. + for (const_iterator it = begin(); it != end_default(); ++it) { + if (!it->isDegenerate()) { + ret.push(it->toSBasis(), i++); + degenerate = false; + } + } + if (degenerate) { + // if path only contains degenerate curves, no second cut is added + // so we need to create at least one segment manually + ret = Piecewise<D2<SBasis> >(initialPoint()); + } + return ret; +} + +template <typename iter> iter inc(iter const &x, unsigned n) { - iter ret = x; - for(unsigned i = 0; i < n; i++) - ret++; - return ret; -} - -Path &Path::operator*=(Affine const &m) { - unshare(); - Sequence::iterator last = get_curves().end() - 1; - Sequence::iterator it; - Point prev; - for (it = get_curves().begin() ; it != last ; ++it) { - *it = boost::shared_ptr<Curve>((*it)->transformed(m)); - if ( it != get_curves().begin() && (*it)->initialPoint() != prev ) { - THROW_CONTINUITYERROR(); - } - prev = (*it)->finalPoint(); - } - for ( int i = 0 ; i < 2 ; ++i ) { - final_->setPoint(i, (*final_)[i] * m); - } - if (get_curves().size() > 1) { - if ( front().initialPoint() != initialPoint() || back().finalPoint() != finalPoint() ) { - THROW_CONTINUITYERROR(); - } - } - return *this; -} - -Path &Path::operator*=(Translate const &m) { -/* Somehow there is something wrong here, LPE Construct grid fails with this code - unshare(); - Sequence::iterator last = get_curves().end() - 1; - Sequence::iterator it; - Point prev; - for (it = get_curves().begin() ; it != last ; ++it) { - // *(const_cast<Curve*>(&**it)) *= m; - const_cast<Curve*>(it->get())->operator*=(m); - if ( it != get_curves().begin() && (*it)->initialPoint() != prev ) { - THROW_CONTINUITYERROR(); - } - prev = (*it)->finalPoint(); - } - for ( int i = 0 ; i < 2 ; ++i ) { - final_->setPoint(i, (*final_)[i] + m.vector()); - } - if (get_curves().size() > 1) { - if ( front().initialPoint() != initialPoint() || back().finalPoint() != finalPoint() ) { - THROW_CONTINUITYERROR(); - } - } - return *this; -*/ - return this->operator*=(static_cast<Affine>(m)); -} - -std::vector<double> -Path::allNearestPoints(Point const& _point, double from, double to) const -{ - using std::swap; - - if ( from > to ) swap(from, to); - const Path& _path = *this; - unsigned int sz = _path.size(); - if ( _path.closed() ) ++sz; - if ( from < 0 || to > sz ) - { - THROW_RANGEERROR("[from,to] interval out of bounds"); - } - double sif, st = modf(from, &sif); - double eif, et = modf(to, &eif); - unsigned int si = static_cast<unsigned int>(sif); - unsigned int ei = static_cast<unsigned int>(eif); - if ( si == sz ) - { - --si; - st = 1; - } - if ( ei == sz ) - { - --ei; - et = 1; - } - if ( si == ei ) - { - std::vector<double> all_nearest = - _path[si].allNearestPoints(_point, st, et); - for ( unsigned int i = 0; i < all_nearest.size(); ++i ) - { - all_nearest[i] = si + all_nearest[i]; - } - return all_nearest; - } - std::vector<double> all_t; - std::vector< std::vector<double> > all_np; - all_np.push_back( _path[si].allNearestPoints(_point, st) ); - std::vector<unsigned int> ni; - ni.push_back(si); - double dsq; - double mindistsq - = distanceSq( _point, _path[si].pointAt( all_np.front().front() ) ); - Rect bb(Geom::Point(0,0),Geom::Point(0,0)); - for ( unsigned int i = si + 1; i < ei; ++i ) - { - bb = (_path[i].boundsFast()); - dsq = distanceSq(_point, bb); - if ( mindistsq < dsq ) continue; - all_t = _path[i].allNearestPoints(_point); - dsq = distanceSq( _point, _path[i].pointAt( all_t.front() ) ); - if ( mindistsq > dsq ) - { - all_np.clear(); - all_np.push_back(all_t); - ni.clear(); - ni.push_back(i); - mindistsq = dsq; - } - else if ( mindistsq == dsq ) - { - all_np.push_back(all_t); - ni.push_back(i); - } - } - bb = (_path[ei].boundsFast()); - dsq = distanceSq(_point, bb); - if ( mindistsq >= dsq ) - { - all_t = _path[ei].allNearestPoints(_point, 0, et); - dsq = distanceSq( _point, _path[ei].pointAt( all_t.front() ) ); - if ( mindistsq > dsq ) - { - for ( unsigned int i = 0; i < all_t.size(); ++i ) - { - all_t[i] = ei + all_t[i]; - } - return all_t; - } - else if ( mindistsq == dsq ) - { - all_np.push_back(all_t); - ni.push_back(ei); - } - } - std::vector<double> all_nearest; - for ( unsigned int i = 0; i < all_np.size(); ++i ) - { - for ( unsigned int j = 0; j < all_np[i].size(); ++j ) - { - all_nearest.push_back( ni[i] + all_np[i][j] ); - } - } - all_nearest.erase(std::unique(all_nearest.begin(), all_nearest.end()), - all_nearest.end()); - return all_nearest; -} - -std::vector<double> -Path::nearestPointPerCurve(Point const& _point) const -{ - //return a single nearest point for each curve in this path - std::vector<double> np; - for (const_iterator it = begin() ; it != end_default() ; ++it) - //for (std::vector<Path>::const_iterator it = _path.begin(); it != _path.end(), ++it){ - { - np.push_back(it->nearestPoint(_point)); - } - return np; -} - -double Path::nearestPoint(Point const &_point, double from, double to, double *distance_squared) const -{ - using std::swap; - - if ( from > to ) swap(from, to); - const Path& _path = *this; - unsigned int sz = _path.size(); - if ( _path.closed() ) ++sz; - if ( from < 0 || to > sz ) - { - THROW_RANGEERROR("[from,to] interval out of bounds"); - } - double sif, st = modf(from, &sif); - double eif, et = modf(to, &eif); - unsigned int si = static_cast<unsigned int>(sif); - unsigned int ei = static_cast<unsigned int>(eif); - if(sz == 0) {// naked moveto - if (distance_squared != NULL) - *distance_squared = distanceSq(_point, _path.initialPoint()); - return 0; - } - if ( si == sz ) - { - --si; - st = 1; - } - if ( ei == sz ) - { - --ei; - et = 1; - } - if ( si == ei ) - { - double nearest = _path[si].nearestPoint(_point, st, et); - if (distance_squared != NULL) - *distance_squared = distanceSq(_point, _path[si].pointAt(nearest)); - return si + nearest; - } - - double t; - double nearest = _path[si].nearestPoint(_point, st); - unsigned int ni = si; - double dsq; - double mindistsq = distanceSq(_point, _path[si].pointAt(nearest)); - for ( unsigned int i = si + 1; i < ei; ++i ) - { - Rect bb = (_path[i].boundsFast()); - dsq = distanceSq(_point, bb); - if ( mindistsq <= dsq ) continue; - t = _path[i].nearestPoint(_point); - dsq = distanceSq(_point, _path[i].pointAt(t)); - if ( mindistsq > dsq ) - { - nearest = t; - ni = i; - mindistsq = dsq; - } - } - Rect bb = (_path[ei].boundsFast()); - dsq = distanceSq(_point, bb); - if ( mindistsq > dsq ) - { - t = _path[ei].nearestPoint(_point, 0, et); - dsq = distanceSq(_point, _path[ei].pointAt(t)); - if ( mindistsq > dsq ) - { - nearest = t; - ni = ei; - mindistsq = dsq; - } - } - - if (distance_squared != NULL) - *distance_squared = mindistsq; - - return ni + nearest; -} - -void Path::appendPortionTo(Path &ret, double from, double to) const { - if (!(from >= 0 && to >= 0)) { - THROW_RANGEERROR("from and to must be >=0 in Path::appendPortionTo"); - } - if(to == 0) to = size()+0.999999; - if(from == to) { return; } - double fi, ti; - double ff = modf(from, &fi), tf = modf(to, &ti); - if(tf == 0) { ti--; tf = 1; } - const_iterator fromi = inc(begin(), (unsigned)fi); - if(fi == ti && from < to) { - Curve *v = fromi->portion(ff, tf); - ret.append(*v, STITCH_DISCONTINUOUS); - delete v; - return; - } - const_iterator toi = inc(begin(), (unsigned)ti); - if(ff != 1.) { - Curve *fromv = fromi->portion(ff, 1.); - //fromv->setInitial(ret.finalPoint()); - ret.append(*fromv, STITCH_DISCONTINUOUS); - delete fromv; - } - if(from >= to) { - const_iterator ender = end(); - if(ender->initialPoint() == ender->finalPoint()) ++ender; - ret.insert(ret.end(), ++fromi, ender, STITCH_DISCONTINUOUS); - ret.insert(ret.end(), begin(), toi, STITCH_DISCONTINUOUS); - } else { - ret.insert(ret.end(), ++fromi, toi, STITCH_DISCONTINUOUS); - } - Curve *tov = toi->portion(0., tf); - ret.append(*tov, STITCH_DISCONTINUOUS); - delete tov; -} - -void Path::do_update(Sequence::iterator first_replaced, - Sequence::iterator last_replaced, - Sequence::iterator first, - Sequence::iterator last) -{ - // note: modifies the contents of [first,last) - check_continuity(first_replaced, last_replaced, first, last); - if ( ( last - first ) == ( last_replaced - first_replaced ) ) { - std::copy(first, last, first_replaced); - } else { - // this approach depends on std::vector's behavior WRT iterator stability - get_curves().erase(first_replaced, last_replaced); - get_curves().insert(first_replaced, first, last); - } - - if ( get_curves().front().get() != final_ ) { - final_->setPoint(0, back().finalPoint()); - final_->setPoint(1, front().initialPoint()); - } -} - -void Path::do_append(Curve *c) { - if ( get_curves().front().get() == final_ ) { - final_->setPoint(1, c->initialPoint()); - } else { - if (c->initialPoint() != finalPoint()) { - THROW_CONTINUITYERROR(); - } - } - get_curves().insert(get_curves().end()-1, boost::shared_ptr<Curve>(c)); - final_->setPoint(0, c->finalPoint()); -} - -void Path::stitch(Sequence::iterator first_replaced, - Sequence::iterator last_replaced, - Sequence &source) -{ - if (!source.empty()) { - if ( first_replaced != get_curves().begin() ) { - if ( (*first_replaced)->initialPoint() != source.front()->initialPoint() ) { - Curve *stitch = new StitchSegment((*first_replaced)->initialPoint(), - source.front()->initialPoint()); - source.insert(source.begin(), boost::shared_ptr<Curve>(stitch)); - } - } - if ( last_replaced != (get_curves().end()-1) ) { - if ( (*last_replaced)->finalPoint() != source.back()->finalPoint() ) { - Curve *stitch = new StitchSegment(source.back()->finalPoint(), - (*last_replaced)->finalPoint()); - source.insert(source.end(), boost::shared_ptr<Curve>(stitch)); - } - } - } else if ( first_replaced != last_replaced && first_replaced != get_curves().begin() && last_replaced != get_curves().end()-1) { - if ( (*first_replaced)->initialPoint() != (*(last_replaced-1))->finalPoint() ) { - Curve *stitch = new StitchSegment((*(last_replaced-1))->finalPoint(), - (*first_replaced)->initialPoint()); - source.insert(source.begin(), boost::shared_ptr<Curve>(stitch)); - } - } -} - -void Path::check_continuity(Sequence::iterator first_replaced, - Sequence::iterator last_replaced, - Sequence::iterator first, - Sequence::iterator last) -{ - if ( first != last ) { - if ( first_replaced != get_curves().begin() ) { - if ( (*first_replaced)->initialPoint() != (*first)->initialPoint() ) { - THROW_CONTINUITYERROR(); - } + iter ret = x; + for (unsigned i = 0; i < n; i++) + ret++; + return ret; +} + +bool Path::operator==(Path const &other) const +{ + if (this == &other) + return true; + if (_closed != other._closed) + return false; + return *_curves == *other._curves; +} + +void Path::start(Point const &p) { + if (_curves->size() > 1) { + clear(); + } + _closing_seg->setInitial(p); + _closing_seg->setFinal(p); +} + +Interval Path::timeRange() const +{ + Interval ret(0, size_default()); + return ret; +} + +Curve const &Path::curveAt(Coord t, Coord *rest) const +{ + PathTime pos = _factorTime(t); + if (rest) { + *rest = pos.t; + } + return at(pos.curve_index); +} + +Point Path::pointAt(Coord t) const +{ + return pointAt(_factorTime(t)); +} + +Coord Path::valueAt(Coord t, Dim2 d) const +{ + return valueAt(_factorTime(t), d); +} + +Curve const &Path::curveAt(PathTime const &pos) const +{ + return at(pos.curve_index); +} +Point Path::pointAt(PathTime const &pos) const +{ + return at(pos.curve_index).pointAt(pos.t); +} +Coord Path::valueAt(PathTime const &pos, Dim2 d) const +{ + return at(pos.curve_index).valueAt(pos.t, d); +} + +std::vector<PathTime> Path::roots(Coord v, Dim2 d) const +{ + std::vector<PathTime> res; + for (unsigned i = 0; i <= size(); i++) { + std::vector<Coord> temp = (*this)[i].roots(v, d); + for (unsigned j = 0; j < temp.size(); j++) + res.push_back(PathTime(i, temp[j])); + } + return res; +} + + +// The class below implements sweepline optimization for curve intersection in paths. +// Instead of O(N^2), this takes O(N + X), where X is the number of overlaps +// between the bounding boxes of curves. + +struct CurveSweepTraits { + struct Bound { + Rect r; + std::size_t index; + int which; + }; + typedef std::less<Coord> Compare; + inline static Coord entry_value(Bound const &b) { return b.r[X].min(); } + inline static Coord exit_value(Bound const &b) { return b.r[X].max(); } +}; + +class CurveSweeper + : public Sweeper<Curve const *, CurveSweepTraits> +{ +public: + CurveSweeper(Path const &a, Path const &b, std::vector<PathIntersection> &result, Coord prec) + : _result(result) + , _precision(prec) + { + for (std::size_t i = 0; i < a.size(); ++i) { + Bound bound; + bound.r = a[i].boundsFast(); + bound.index = i; + bound.which = 0; + insert(bound, &a[i]); + } + for (std::size_t i = 0; i < b.size(); ++i) { + Bound bound; + bound.r = b[i].boundsFast(); + bound.index = i; + bound.which = 1; + insert(bound, &b[i]); + } + } + +protected: + void _enter(Record const &record) { + int which = record.bound.which; + + for (RecordList::iterator i = _active_items.begin(); i != _active_items.end(); ++i) { + // do not intersect in the same path + if (i->bound.which == which) continue; + // do not intersect if boxes do not overlap in Y + if (!record.bound.r[Y].intersects(i->bound.r[Y])) continue; + + std::vector<CurveIntersection> cx; + int ia = record.bound.index; + int ib = i->bound.index; + + if (which == 0) { + cx = record.item->intersect(*i->item, _precision); + } else { + cx = i->item->intersect(*record.item, _precision); + std::swap(ia, ib); + } + + for (std::size_t ci = 0; ci < cx.size(); ++ci) { + PathTime a(ia, cx[ci].first), b(ib, cx[ci].second); + PathIntersection px(a, b, cx[ci].point()); + _result.push_back(px); + } + } + } + +private: + std::vector<PathIntersection> &_result; + Coord _precision; +}; + +std::vector<PathIntersection> Path::intersect(Path const &other, Coord precision) const +{ + std::vector<PathIntersection> result; + + CurveSweeper sweeper(*this, other, result, precision); + sweeper.process(); + + // preprocessing to remove duplicate intersections at endpoints + for (std::size_t i = 0; i < result.size(); ++i) { + result[i].first.normalizeForward(size()); + result[i].second.normalizeForward(other.size()); + } + std::sort(result.begin(), result.end()); + result.erase(std::unique(result.begin(), result.end()), result.end()); + + return result; +} + +int Path::winding(Point const &p) const { + int wind = 0; + + /* To handle all the edge cases, we consider the maximum Y edge of the bounding box + * as not included in box. This way paths that contain linear horizontal + * segments will be treated correctly. */ + for (const_iterator i = begin(); i != end_closed(); ++i) { + Rect bounds = i->boundsFast(); + + if (bounds.height() == 0) continue; + if (p[X] > bounds.right() || !bounds[Y].lowerContains(p[Y])) { + // Ray doesn't intersect bbox, so we ignore this segment + continue; + } + + if (p[X] < bounds.left()) { + /* Ray intersects the curve's bbox, but the point is outside it. + * The winding contribution is exactly the same as that + * of a linear segment with the same initial and final points. */ + Point ip = i->initialPoint(); + Point fp = i->finalPoint(); + Rect eqbox(ip, fp); + + if (eqbox[Y].lowerContains(p[Y])) { + /* The ray intersects the equivalent linear segment. + * Determine winding contribution based on its derivative. */ + if (ip[Y] < fp[Y]) { + wind += 1; + } else if (ip[Y] > fp[Y]) { + wind -= 1; + } else { + // should never happen, because bounds.height() was not zero + assert(false); + } + } + } else { + // point is inside bbox + wind += i->winding(p); + } + } + return wind; +} + +std::vector<double> Path::allNearestTimes(Point const &_point, double from, double to) const +{ + // TODO from and to are not used anywhere. + // rewrite this to simplify. + using std::swap; + + if (from > to) + swap(from, to); + const Path &_path = *this; + unsigned int sz = _path.size(); + if (_path.closed()) + ++sz; + if (from < 0 || to > sz) { + THROW_RANGEERROR("[from,to] interval out of bounds"); + } + double sif, st = modf(from, &sif); + double eif, et = modf(to, &eif); + unsigned int si = static_cast<unsigned int>(sif); + unsigned int ei = static_cast<unsigned int>(eif); + if (si == sz) { + --si; + st = 1; + } + if (ei == sz) { + --ei; + et = 1; + } + if (si == ei) { + std::vector<double> all_nearest = _path[si].allNearestTimes(_point, st, et); + for (unsigned int i = 0; i < all_nearest.size(); ++i) { + all_nearest[i] = si + all_nearest[i]; + } + return all_nearest; + } + std::vector<double> all_t; + std::vector<std::vector<double> > all_np; + all_np.push_back(_path[si].allNearestTimes(_point, st)); + std::vector<unsigned int> ni; + ni.push_back(si); + double dsq; + double mindistsq = distanceSq(_point, _path[si].pointAt(all_np.front().front())); + Rect bb(Geom::Point(0, 0), Geom::Point(0, 0)); + for (unsigned int i = si + 1; i < ei; ++i) { + bb = (_path[i].boundsFast()); + dsq = distanceSq(_point, bb); + if (mindistsq < dsq) + continue; + all_t = _path[i].allNearestTimes(_point); + dsq = distanceSq(_point, _path[i].pointAt(all_t.front())); + if (mindistsq > dsq) { + all_np.clear(); + all_np.push_back(all_t); + ni.clear(); + ni.push_back(i); + mindistsq = dsq; + } else if (mindistsq == dsq) { + all_np.push_back(all_t); + ni.push_back(i); + } + } + bb = (_path[ei].boundsFast()); + dsq = distanceSq(_point, bb); + if (mindistsq >= dsq) { + all_t = _path[ei].allNearestTimes(_point, 0, et); + dsq = distanceSq(_point, _path[ei].pointAt(all_t.front())); + if (mindistsq > dsq) { + for (unsigned int i = 0; i < all_t.size(); ++i) { + all_t[i] = ei + all_t[i]; + } + return all_t; + } else if (mindistsq == dsq) { + all_np.push_back(all_t); + ni.push_back(ei); + } + } + std::vector<double> all_nearest; + for (unsigned int i = 0; i < all_np.size(); ++i) { + for (unsigned int j = 0; j < all_np[i].size(); ++j) { + all_nearest.push_back(ni[i] + all_np[i][j]); + } + } + all_nearest.erase(std::unique(all_nearest.begin(), all_nearest.end()), all_nearest.end()); + return all_nearest; +} + +std::vector<Coord> Path::nearestTimePerCurve(Point const &p) const +{ + // return a single nearest time for each curve in this path + std::vector<Coord> np; + for (const_iterator it = begin(); it != end_default(); ++it) { + np.push_back(it->nearestTime(p)); + } + return np; +} + +PathTime Path::nearestTime(Point const &p, Coord *dist) const +{ + Coord mindist = std::numeric_limits<Coord>::max(); + PathTime ret; + + if (_curves->size() == 1) { + // naked moveto + ret.curve_index = 0; + ret.t = 0; + if (dist) { + *dist = distance(_closing_seg->initialPoint(), p); + } + return ret; + } + + for (size_type i = 0; i < size_default(); ++i) { + Curve const &c = at(i); + if (distance(p, c.boundsFast()) >= mindist) continue; + + Coord t = c.nearestTime(p); + Coord d = distance(c.pointAt(t), p); + if (d < mindist) { + mindist = d; + ret.curve_index = i; + ret.t = t; + } + } + if (dist) { + *dist = mindist; + } + + return ret; +} + +void Path::appendPortionTo(Path &ret, double from, double to) const +{ + if (!(from >= 0 && to >= 0)) { + THROW_RANGEERROR("from and to must be >=0 in Path::appendPortionTo"); + } + if (to == 0) + to = size() + 0.999999; + if (from == to) { + return; + } + double fi, ti; + double ff = modf(from, &fi), tf = modf(to, &ti); + if (tf == 0) { + ti--; + tf = 1; + } + const_iterator fromi = inc(begin(), (unsigned)fi); + if (fi == ti && from < to) { + ret.append(fromi->portion(ff, tf)); + return; + } + const_iterator toi = inc(begin(), (unsigned)ti); + if (ff != 1.) { + // fromv->setInitial(ret.finalPoint()); + ret.append(fromi->portion(ff, 1.)); + } + if (from >= to) { + const_iterator ender = end(); + if (ender->initialPoint() == ender->finalPoint()) + ++ender; + ret.insert(ret.end(), ++fromi, ender); + ret.insert(ret.end(), begin(), toi); + } else { + ret.insert(ret.end(), ++fromi, toi); + } + ret.append(toi->portion(0., tf)); +} + +void Path::appendPortionTo(Path &target, PathInterval const &ival, + boost::optional<Point> const &p_from, boost::optional<Point> const &p_to) const +{ + assert(ival.pathSize() == size_closed()); + + if (ival.isDegenerate()) { + Point stitch_to = p_from ? *p_from : pointAt(ival.from()); + target.stitchTo(stitch_to); + return; + } + + PathTime const &from = ival.from(), &to = ival.to(); + + bool reverse = ival.reverse(); + int di = reverse ? -1 : 1; + size_type s = size_closed(); + + if (!ival.crossesStart() && from.curve_index == to.curve_index) { + Curve *c = (*this)[from.curve_index].portion(from.t, to.t); + if (p_from) { + c->setInitial(*p_from); + } + if (p_to) { + c->setFinal(*p_to); + } + target.append(c); + } else { + Curve *c_first = (*this)[from.curve_index].portion(from.t, reverse ? 0 : 1); + if (p_from) { + c_first->setInitial(*p_from); + } + target.append(c_first); + + for (size_type i = (from.curve_index + s + di) % s; i != to.curve_index; + i = (i + s + di) % s) + { + if (reverse) { + target.append((*this)[i].reverse()); + } else { + target.append((*this)[i].duplicate()); + } + } + + Curve *c_last = (*this)[to.curve_index].portion(reverse ? 1 : 0, to.t); + if (p_to) { + c_last->setFinal(*p_to); + } + target.append(c_last); + } +} + +Path Path::reversed() const +{ + typedef std::reverse_iterator<Sequence::const_iterator> RIter; + + Path ret(finalPoint()); + if (empty()) return ret; + + ret._curves->pop_back(); // this also deletes the closing segment from ret + + RIter iter(_includesClosingSegment() ? _curves->end() : _curves->end() - 1); + RIter rend(_curves->begin()); + + if (_closed) { + // when the path is closed, there are two cases: + if (front().isLineSegment()) { + // 1. initial segment is linear: it becomes the new closing segment. + rend = RIter(_curves->begin() + 1); + ret._closing_seg = new ClosingSegment(front().finalPoint(), front().initialPoint()); + } else { + // 2. initial segment is not linear: the closing segment becomes degenerate. + // However, skip it if it's already degenerate. + Point fp = finalPoint(); + ret._closing_seg = new ClosingSegment(fp, fp); + } + } else { + // when the path is open, we reverse all real curves, and add a reversed closing segment. + ret._closing_seg = static_cast<ClosingSegment *>(_closing_seg->reverse()); + } + + for (; iter != rend; ++iter) { + ret._curves->push_back(iter->reverse()); + } + ret._curves->push_back(ret._closing_seg); + ret._closed = _closed; + return ret; +} + + +void Path::insert(iterator pos, Curve const &curve) +{ + _unshare(); + Sequence::iterator seq_pos(seq_iter(pos)); + Sequence source; + source.push_back(curve.duplicate()); + do_update(seq_pos, seq_pos, source); +} + +void Path::erase(iterator pos) +{ + _unshare(); + Sequence::iterator seq_pos(seq_iter(pos)); + + Sequence stitched; + do_update(seq_pos, seq_pos + 1, stitched); +} + +void Path::erase(iterator first, iterator last) +{ + _unshare(); + Sequence::iterator seq_first = seq_iter(first); + Sequence::iterator seq_last = seq_iter(last); + + Sequence stitched; + do_update(seq_first, seq_last, stitched); +} + +void Path::stitchTo(Point const &p) +{ + if (!empty() && _closing_seg->initialPoint() != p) { + if (_exception_on_stitch) { + THROW_CONTINUITYERROR(); + } + _unshare(); + do_append(new StitchSegment(_closing_seg->initialPoint(), p)); } - if ( last_replaced != (get_curves().end()-1) ) { - if ( (*(last_replaced-1))->finalPoint() != (*(last-1))->finalPoint() ) { +} + +void Path::replace(iterator replaced, Curve const &curve) +{ + replace(replaced, replaced + 1, curve); +} + +void Path::replace(iterator first_replaced, iterator last_replaced, Curve const &curve) +{ + _unshare(); + Sequence::iterator seq_first_replaced(seq_iter(first_replaced)); + Sequence::iterator seq_last_replaced(seq_iter(last_replaced)); + Sequence source(1); + source.push_back(curve.duplicate()); + + do_update(seq_first_replaced, seq_last_replaced, source); +} + +void Path::replace(iterator replaced, Path const &path) +{ + replace(replaced, path.begin(), path.end()); +} + +void Path::replace(iterator first, iterator last, Path const &path) +{ + replace(first, last, path.begin(), path.end()); +} + +void Path::snapEnds(Coord precision) +{ + if (!_closed) return; + if (_curves->size() > 1 && are_near(_closing_seg->length(precision), 0, precision)) { + _unshare(); + _closing_seg->setInitial(_closing_seg->finalPoint()); + (_curves->end() - 1)->setFinal(_closing_seg->finalPoint()); + } +} + +// replace curves between first and last with contents of source, +// +void Path::do_update(Sequence::iterator first, Sequence::iterator last, Sequence &source) +{ + // TODO: handle cases where first > last in closed paths? + bool last_beyond_closing_segment = (last == _curves->end()); + + // special case: + // if do_update replaces the closing segment, we have to regenerate it + if (source.empty()) { + if (first == last) return; // nothing to do + + // only removing some segments + if ((!_closed && first == _curves->begin()) || (!_closed && last == _curves->end() - 1) || last_beyond_closing_segment) { + // just adjust the closing segment + // do nothing + } else if (first->initialPoint() != (last - 1)->finalPoint()) { + if (_exception_on_stitch) { + THROW_CONTINUITYERROR(); + } + source.push_back(new StitchSegment(first->initialPoint(), (last - 1)->finalPoint())); + } + } else { + // replacing + if (first == _curves->begin() && last == _curves->end()) { + // special case: replacing everything should work the same in open and closed curves + _curves->erase(_curves->begin(), _curves->end() - 1); + _closing_seg->setFinal(source.front().initialPoint()); + _closing_seg->setInitial(source.back().finalPoint()); + _curves->transfer(_curves->begin(), source.begin(), source.end(), source); + return; + } + + // stitch in front + if (!_closed && first == _curves->begin()) { + // not necessary to stitch in front + } else if (first->initialPoint() != source.front().initialPoint()) { + if (_exception_on_stitch) { + THROW_CONTINUITYERROR(); + } + source.insert(source.begin(), new StitchSegment(first->initialPoint(), source.front().initialPoint())); + } + + // stitch at the end + if ((!_closed && last == _curves->end() - 1) || last_beyond_closing_segment) { + // repurpose the closing segment as the stitch segment + // do nothing + } else if (source.back().finalPoint() != (last - 1)->finalPoint()) { + if (_exception_on_stitch) { + THROW_CONTINUITYERROR(); + } + source.push_back(new StitchSegment(source.back().finalPoint(), (last - 1)->finalPoint())); + } + } + + // do not erase the closing segment, adjust it instead + if (last_beyond_closing_segment) { + --last; + } + _curves->erase(first, last); + _curves->transfer(first, source.begin(), source.end(), source); + + // adjust closing segment + if (size_open() == 0) { + _closing_seg->setFinal(_closing_seg->initialPoint()); + } else { + _closing_seg->setInitial(back_open().finalPoint()); + _closing_seg->setFinal(front().initialPoint()); + } + + checkContinuity(); +} + +void Path::do_append(Curve *c) +{ + if (&_curves->front() == _closing_seg) { + _closing_seg->setFinal(c->initialPoint()); + } else { + // if we can't freely move the closing segment, we check whether + // the new curve connects with the last non-closing curve + if (c->initialPoint() != _closing_seg->initialPoint()) { + THROW_CONTINUITYERROR(); + } + if (_closed && c->isLineSegment() && + c->finalPoint() == _closing_seg->finalPoint()) + { + // appending a curve that matches the closing segment has no effect + delete c; + return; + } + } + _curves->insert(_curves->end() - 1, c); + _closing_seg->setInitial(c->finalPoint()); +} + +void Path::checkContinuity() const +{ + Sequence::const_iterator i = _curves->begin(), j = _curves->begin(); + ++j; + for (; j != _curves->end(); ++i, ++j) { + if (i->finalPoint() != j->initialPoint()) { + THROW_CONTINUITYERROR(); + } + } + if (_curves->front().initialPoint() != _curves->back().finalPoint()) { THROW_CONTINUITYERROR(); - } } - } else if ( first_replaced != last_replaced && first_replaced != get_curves().begin() && last_replaced != get_curves().end()-1) { - if ( (*first_replaced)->initialPoint() != (*(last_replaced-1))->finalPoint() ) { - THROW_CONTINUITYERROR(); +} + +// breaks time value into integral and fractional part +PathTime Path::_factorTime(Coord t) const +{ + size_type sz = size_default(); + if (t < 0 || t > sz) { + THROW_RANGEERROR("parameter t out of bounds"); + } + + PathTime ret; + Coord k; + ret.t = modf(t, &k); + ret.curve_index = k; + if (ret.curve_index == sz) { + --ret.curve_index; + ret.t = 1; } - } + return ret; +} + +Piecewise<D2<SBasis> > paths_to_pw(PathVector const &paths) +{ + Piecewise<D2<SBasis> > ret = paths[0].toPwSb(); + for (unsigned i = 1; i < paths.size(); i++) { + ret.concat(paths[i].toPwSb()); + } + return ret; +} + +std::ostream &operator<<(std::ostream &out, Path const &path) +{ + SVGPathWriter pw; + pw.feed(path); + out << pw.str(); + return out; } } // end namespace Geom diff --git a/src/2geom/path.h b/src/2geom/path.h index 28d2a25e4..3ca43e0e5 100644 --- a/src/2geom/path.h +++ b/src/2geom/path.h @@ -1,43 +1,47 @@ -/** - * \file - * \brief Path - Series of continuous curves +/** @file + * @brief Path - a sequence of contiguous curves *//* - * Authors: - * MenTaLguY <mental@rydia.net> - * Marco Cecchetti <mrcekets at gmail.com> - * - * Copyright 2007-2008 Authors - * - * This library is free software; you can redistribute it and/or - * modify it either under the terms of the GNU Lesser General Public - * License version 2.1 as published by the Free Software Foundation - * (the "LGPL") or, at your option, under the terms of the Mozilla - * Public License Version 1.1 (the "MPL"). If you do not alter this - * notice, a recipient may use your version of this file under either - * the MPL or the LGPL. - * - * You should have received a copy of the LGPL along with this library - * in the file COPYING-LGPL-2.1; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * You should have received a copy of the MPL along with this library - * in the file COPYING-MPL-1.1 - * - * The contents of this file are subject to the Mozilla Public License - * Version 1.1 (the "License"); you may not use this file except in - * compliance with the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY - * OF ANY KIND, either express or implied. See the LGPL or the MPL for - * the specific language governing rights and limitations. - */ + * Authors: + * MenTaLguY <mental@rydia.net> + * Marco Cecchetti <mrcekets at gmail.com> + * Krzysztof Kosiński <tweenk.pl@gmail.com> + * + * Copyright 2007-2014 Authors + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + */ #ifndef LIB2GEOM_SEEN_PATH_H #define LIB2GEOM_SEEN_PATH_H #include <iterator> #include <algorithm> +#include <iostream> +#include <boost/operators.hpp> +#include <boost/ptr_container/ptr_vector.hpp> #include <boost/shared_ptr.hpp> +#include <2geom/intersection.h> #include <2geom/curve.h> #include <2geom/bezier-curve.h> #include <2geom/transforms.h> @@ -45,656 +49,801 @@ namespace Geom { class Path; +class ConvexHull; namespace PathInternal { -typedef std::vector<boost::shared_ptr<Curve const> > Sequence; +typedef boost::ptr_vector<Curve> Sequence; + +template <typename P> +class BaseIterator + : public boost::random_access_iterator_helper + < BaseIterator<P> + , Curve const + , std::ptrdiff_t + , Curve const * + , Curve const & + > +{ + protected: + BaseIterator(P &p, unsigned i) : path(&p), index(i) {} + // default copy, default assign + typedef BaseIterator<P> Self; -template <typename C, typename P> -class BaseIterator { -protected: - BaseIterator() : path(NULL), index(0) {} - BaseIterator(P *p, unsigned i) : path(p), index(i) {} - // default copy, default assign + public: + BaseIterator() : path(NULL), index(0) {} -public: - bool operator==(BaseIterator const &other) { - return path == other.path && index == other.index; - } - bool operator!=(BaseIterator const &other) { - return path != other.path || index != other.index; - } - - Curve const &operator*() const { return (*path)[index]; } - Curve const *operator->() const { return &(*path)[index]; } - boost::shared_ptr<Curve const> get_ref() const { - return path->get_ref_at_index(index); - } - - C &operator++() { - ++index; - return static_cast<C &>(*this); - } - C operator++(int) { - C old(static_cast<C &>(*this)); - ++(*this); - return old; - } - - C &operator--() { - --index; - return static_cast<C &>(*this); - } - C operator--(int) { - C old(static_cast<C &>(*this)); - --(*this); - return old; - } + bool operator<(BaseIterator const &other) const { + return path == other.path && index < other.index; + } + bool operator==(BaseIterator const &other) const { + return path == other.path && index == other.index; + } + Curve const &operator*() const { + return (*path)[index]; + } -private: - P *path; - unsigned index; + Self &operator++() { + ++index; + return *this; + } + Self &operator--() { + --index; + return *this; + } + Self &operator+=(std::ptrdiff_t d) { + index += d; + return *this; + } + Self &operator-=(std::ptrdiff_t d) { + index -= d; + return *this; + } - friend class ::Geom::Path; + private: + P *path; + unsigned index; + + friend class ::Geom::Path; }; -class ConstIterator : public BaseIterator<ConstIterator, Path const> { -public: - typedef BaseIterator<ConstIterator, Path const> Base; +} - ConstIterator() : Base() {} - // default copy, default assign +/** @brief Generalized time value in the path. + * + * This class exists because when mapping the range of multiple curves onto the same interval + * as the curve index, we lose some precision. For instance, a path with 16 curves will + * have 4 bits less precision than a path with 1 curve. If you need high precision results + * in long paths, either use this class and related methods instead of the standard methods + * pointAt(), nearestTime() and so on, or use curveAt() to first obtain the curve, then + * call the method again to obtain a high precision result. + * + * @ingroup Paths */ +struct PathTime + : boost::totally_ordered<PathTime> +{ + typedef PathInternal::Sequence::size_type size_type; -private: - ConstIterator(Path const &p, unsigned i) : Base(&p, i) {} - friend class ::Geom::Path; + Coord t; ///< Time value in the curve + size_type curve_index; ///< Index of the curve in the path + + PathTime() : t(0), curve_index(0) {} + PathTime(size_type idx, Coord tval) : t(tval), curve_index(idx) {} + + bool operator<(PathTime const &other) const { + if (curve_index < other.curve_index) return true; + if (curve_index == other.curve_index) { + return t < other.t; + } + return false; + } + bool operator==(PathTime const &other) const { + return curve_index == other.curve_index && t == other.t; + } + /// Convert times at or beyond 1 to 0 on the next curve. + void normalizeForward(size_type path_size) { + if (t >= 1) { + curve_index = (curve_index + 1) % path_size; + t = 0; + } + } + /// Convert times at or before 0 to 1 on the previous curve. + void normalizeBackward(size_type path_size) { + if (t <= 0) { + curve_index = (curve_index - 1) % path_size; + t = 1; + } + } + + Coord asFlatTime() const { return curve_index + t; } }; -class Iterator : public BaseIterator<Iterator, Path> { -public: - typedef BaseIterator<Iterator, Path> Base; +inline std::ostream &operator<<(std::ostream &os, PathTime const &pos) { + os << pos.curve_index << ": " << format_coord_nice(pos.t); + return os; +} - Iterator() : Base() {} - // default copy, default assign - operator ConstIterator const &() const { - return reinterpret_cast<ConstIterator const &>(*this); - } +/** @brief Contiguous subset of the path's parameter domain. + * This is a directed interval, which allows one to specify any contiguous subset + * of the path's domain, including subsets that wrap around the initial point + * of the path. + * @ingroup Paths */ +class PathInterval { +public: + typedef PathInternal::Sequence::size_type size_type; + + /** @brief Default interval. + * Default-constructed PathInterval includes only the initial point of the initial segment. */ + PathInterval(); + + /** @brief Construct an interval in the path's parameter domain. + * @param from Initial time + * @param to Final time + * @param cross_start If true, the interval will proceed from the initial to final + * time through the initial point of the path, wrapping around the closing segment; + * otherwise it will not wrap around the closing segment. + * @param path_size Size of the path to which this interval applies, required + * to clean up degenerate cases */ + PathInterval(PathTime const &from, PathTime const &to, bool cross_start, size_type path_size); + + /// Get the time value of the initial point. + PathTime const &initialTime() const { return _from; } + /// Get the time value of the final point. + PathTime const &finalTime() const { return _to; } + + PathTime const &from() const { return _from; } + PathTime const &to() const { return _to; } + + /// Check whether the interval has only one point. + bool isDegenerate() const { return _from == _to; } + /// True if the interval goes in the direction of decreasing time values. + bool reverse() const { return _reverse; } + /// True if the interior of the interval contains the initial point of the path. + bool crossesStart() const { return _cross_start; } + + /// Test a path time for inclusion. + bool contains(PathTime const &pos) const; + + /// Get a time at least @a min_dist away in parameter space from the ends. + /// If no such time exists, the middle point is returned. + PathTime inside(Coord min_dist = EPSILON) const; + + /// Select one of two intervals with given endpoints by parameter direction. + static PathInterval from_direction(PathTime const &from, PathTime const &to, + bool reversed, size_type path_size); + + /// Select one of two intervals with given endpoints by whether it includes the initial point. + static PathInterval from_start_crossing(PathTime const &from, PathTime const &to, + bool cross_start, size_type path_size) { + PathInterval result(from, to, cross_start, path_size); + return result; + } + + size_type pathSize() const { return _path_size; } private: - Iterator(Path &p, unsigned i) : Base(&p, i) {} - friend class ::Geom::Path; + PathTime _from, _to; + size_type _path_size; + bool _cross_start, _reverse; }; +/// Create an interval in the direction of increasing time value. +/// @relates PathInterval +inline PathInterval forward_interval(PathTime const &from, PathTime const &to, + PathInterval::size_type path_size) +{ + PathInterval result = PathInterval::from_direction(from, to, false, path_size); + return result; +} + +/// Create an interval in the direction of decreasing time value. +/// @relates PathInterval +inline PathInterval backward_interval(PathTime const &from, PathTime const &to, + PathInterval::size_type path_size) +{ + PathInterval result = PathInterval::from_direction(from, to, true, path_size); + return result; } -/* - * Open and closed paths: all paths, whether open or closed, store a final - * segment which connects the initial and final endpoints of the "real" - * path data. While similar to the "z" in an SVG path, it exists for - * both open and closed paths, and is not considered part of the "normal" - * path data, which is always covered by the range [begin(), end_open()). - * Conversely, the range [begin(), end_closed()) always contains the "extra" - * closing segment. +/// Output an interval in the path's domain. +/// @relates PathInterval +inline std::ostream &operator<<(std::ostream &os, PathInterval const &ival) { + os << "PathInterval["; + if (ival.crossesStart()) { + os << ival.from() << " -> 0: 0.0 -> " << ival.to(); + } else { + os << ival.from() << " -> " << ival.to(); + } + os << "]"; + return os; +} + +typedef Intersection<PathTime> PathIntersection; + +template <> +struct ShapeTraits<Path> { + typedef PathTime TimeType; + typedef PathInterval IntervalType; + typedef Path AffineClosureType; + typedef PathIntersection IntersectionType; +}; + +/** @brief Sequence of contiguous curves, aka spline. + * + * Path represents a sequence of contiguous curves, also known as a spline. + * It corresponds to a "subpath" in SVG terminology. It can represent both + * open and closed subpaths. The final point of each curve is exactly + * equal to the initial point of the next curve. + * + * The path always contains a linear closing segment that connects + * the final point of the last "real" curve to the initial point of the + * first curve. This way the curves form a closed loop even for open paths. + * If the closing segment has nonzero length and the path is closed, it is + * considered a normal part of the path data. There are three distinct sets + * of end iterators one can use to iterate over the segments: + * + * - Iterating between @a begin() and @a end() will iterate over segments + * which are part of the path. + * - Iterating between @a begin() and @a end_closed() + * will always iterate over a closed loop of segments. + * - Iterating between @a begin() and @a end_open() will always skip + * the final linear closing segment. + * + * If the final point of the last "real" segment coincides exactly with the initial + * point of the first segment, the closing segment will be absent from both + * [begin(), end_open()) and [begin(), end_closed()). + * + * Normally, an exception will be thrown when you try to insert a curve + * that makes the path non-continuous. If you are working with unsanitized + * curve data, you can call setStitching(true), which will insert line segments + * to make the path continuous. + * + * Internally, Path uses copy-on-write data. This is done for two reasons: first, + * copying a Curve requires calling a virtual function, so it's a little more expensive + * that normal copying; and second, it reduces the memory cost of copying the path. + * Therefore you can return Path and PathVector from functions without worrying + * about temporary copies. + * + * Note that this class cannot represent arbitrary shapes, which may contain holes. + * To do that, use PathVector, which is more generic. * - * The only difference between a closed and an open path is whether - * end_default() returns end_closed() or end_open(). The idea behind this - * is to let any path be stroked using [begin(), end_default()), and filled - * using [begin(), end_closed()), without requiring a separate "filled" version - * of the path to use for filling. + * It's not very convenient to create a Path directly. To construct paths more easily, + * use PathBuilder. * - * \invariant : curves_ always contains at least one segment. The last segment - * is always of type ClosingSegment. All constructors take care of this. - (curves_.size() > 0) && dynamic_cast<ClosingSegment>(curves_.back()) - */ -class Path { + * @ingroup Paths */ +class Path + : boost::equality_comparable< Path > +{ public: - typedef PathInternal::Sequence Sequence; - typedef PathInternal::Iterator iterator; - typedef PathInternal::ConstIterator const_iterator; - typedef Sequence::size_type size_type; - typedef Sequence::difference_type difference_type; + typedef PathInternal::Sequence Sequence; + typedef PathInternal::BaseIterator<Path> iterator; + typedef PathInternal::BaseIterator<Path const> const_iterator; + typedef Sequence::size_type size_type; + typedef Sequence::difference_type difference_type; + + class ClosingSegment : public LineSegment { + public: + ClosingSegment() : LineSegment() {} + ClosingSegment(Point const &p1, Point const &p2) : LineSegment(p1, p2) {} + virtual Curve *duplicate() const { return new ClosingSegment(*this); } + virtual Curve *reverse() const { return new ClosingSegment((*this)[1], (*this)[0]); } + }; + + class StitchSegment : public LineSegment { + public: + StitchSegment() : LineSegment() {} + StitchSegment(Point const &p1, Point const &p2) : LineSegment(p1, p2) {} + virtual Curve *duplicate() const { return new StitchSegment(*this); } + virtual Curve *reverse() const { return new StitchSegment((*this)[1], (*this)[0]); } + }; + + // Path(Path const &other) - use default copy constructor + + /// Construct an empty path starting at the specified point. + explicit Path(Point const &p = Point()) + : _curves(new Sequence()) + , _closing_seg(new ClosingSegment(p, p)) + , _closed(false) + , _exception_on_stitch(true) + { + _curves->push_back(_closing_seg); + } - class ClosingSegment : public LineSegment { - public: - ClosingSegment() : LineSegment() {} - ClosingSegment(Point const &p1, Point const &p2) : LineSegment(p1, p2) {} - virtual Curve *duplicate() const { return new ClosingSegment(*this); } - virtual Curve *reverse() const { return new ClosingSegment((*this)[1], (*this)[0]); } - }; - - enum Stitching { - NO_STITCHING=0, - STITCH_DISCONTINUOUS - }; - - class StitchSegment : public LineSegment { - public: - StitchSegment() : LineSegment() {} - StitchSegment(Point const &p1, Point const &p2) : LineSegment(p1, p2) {} - virtual Curve *duplicate() const { return new StitchSegment(*this); } - virtual Curve *reverse() const { return new StitchSegment((*this)[1], (*this)[0]); } - }; - - // Path(Path const &other) - use default copy constructor - - explicit Path(Point p=Point()) - : curves_(boost::shared_ptr<Sequence>(new Sequence(1, boost::shared_ptr<Curve>()))), - final_(new ClosingSegment(p, p)), - closed_(false) - { - get_curves().back() = boost::shared_ptr<Curve>(final_); - } - - Path(const_iterator const &first, - const_iterator const &last, - bool closed=false) - : curves_(boost::shared_ptr<Sequence>(new Sequence(seq_iter(first), - seq_iter(last)))), - closed_(closed) - { - if (!get_curves().empty()) { - final_ = new ClosingSegment(get_curves().back()->finalPoint(), - get_curves().front()->initialPoint()); - } else { - final_ = new ClosingSegment(); - } - get_curves().push_back(boost::shared_ptr<Curve>(final_)); - } - - virtual ~Path() {} - - // Path &operator=(Path const &other) - use default assignment operator - - /// \todo Add noexcept specifiers for C++11 - void swap(Path &other) { - using std::swap; - swap(other.curves_, curves_); - swap(other.final_, final_); - swap(other.closed_, closed_); - } - friend inline void swap(Path &a, Path &b) { a.swap(b); } - - Curve const &operator[](unsigned i) const { return *get_curves()[i]; } - Curve const &at_index(unsigned i) const { return *get_curves()[i]; } - boost::shared_ptr<Curve const> get_ref_at_index(unsigned i) { - return get_curves()[i]; - } - - Curve const &front() const { return *get_curves()[0]; } - Curve const &back() const { return back_open(); } - Curve const &back_open() const { - if (empty()) { THROW_RANGEERROR("Path contains not enough segments"); } - return *get_curves()[get_curves().size()-2]; - } - Curve const &back_closed() const { return *get_curves()[get_curves().size()-1]; } - Curve const &back_default() const { - return ( closed_ ? back_closed() : back_open() ); - } - - const_iterator begin() const { return const_iterator(*this, 0); } - const_iterator end() const { return const_iterator(*this, size()); } - iterator begin() { return iterator(*this, 0); } - iterator end() { return iterator(*this, size()); } - - const_iterator end_open() const { return const_iterator(*this, size()); } - const_iterator end_closed() const { return const_iterator(*this, size()+1); } - const_iterator end_default() const { - return ( closed_ ? end_closed() : end_open() ); - } - - size_type size_open() const { return get_curves().size()-1; } - size_type size_closed() const { return get_curves().size(); } - size_type size_default() const { - return ( closed_ ? size_closed() : size_open() ); - } - size_type size() const { return size_open(); } - - size_type max_size() const { return get_curves().max_size()-1; } - - bool empty() const { return (get_curves().size() == 1); } - bool closed() const { return closed_; } - void close(bool closed=true) { closed_ = closed; } - - OptRect boundsFast() const; - OptRect boundsExact() const; - - Piecewise<D2<SBasis> > toPwSb() const { - Piecewise<D2<SBasis> > ret; - ret.push_cut(0); - unsigned i = 1; - bool degenerate = true; - // pw<d2<>> is always open. so if path is closed, add closing segment as well to pwd2. - for(const_iterator it = begin(); it != end_default(); ++it) { - if (!it->isDegenerate()) { - ret.push(it->toSBasis(), i++); - degenerate = false; - } - } - if (degenerate) { - // if path only contains degenerate curves, no second cut is added - // so we need to create at least one segment manually - ret = Piecewise<D2<SBasis> >(initialPoint()); - } - return ret; - } - - bool operator==(Path const &other) const { - if (this == &other) return true; - if (closed_ != other.closed_) return false; - return get_curves() == other.get_curves(); - } - bool operator!=(Path const &other) const { - return !( *this == other ); - } - - Path operator*(Affine const &m) const { - Path ret(*this); - ret *= m; - return ret; - } - Path operator*(Translate const &m) const { // specialization over Affine, for faster computation - Path ret(*this); - ret *= m; - return ret; - } - - Path &operator*=(Affine const &m); - Path &operator*=(Translate const &m); // specialization over Affine, for faster computation - - Point pointAt(double t) const - { - unsigned int sz = size(); - if ( closed() ) ++sz; - if ( t < 0 || t > sz ) - { - THROW_RANGEERROR("parameter t out of bounds"); - } - if ( empty() ) return initialPoint(); // naked moveto - double k, lt = modf(t, &k); - unsigned int i = static_cast<unsigned int>(k); - if ( i == sz ) - { - --i; - lt = 1; - } - return (*this)[i].pointAt(lt); - } - - double valueAt(double t, Dim2 d) const - { - unsigned int sz = size(); - if ( closed() ) ++sz; - if ( t < 0 || t > sz ) - { - THROW_RANGEERROR("parameter t out of bounds"); - } - if ( empty() ) return initialPoint()[d]; // naked moveto - double k, lt = modf(t, &k); - unsigned int i = static_cast<unsigned int>(k); - if ( i == sz ) - { - --i; - lt = 1; - } - return (*this)[i].valueAt(lt, d); - } - - - Point operator() (double t) const - { - return pointAt(t); - } - - std::vector<double> roots(double v, Dim2 d) const { - std::vector<double> res; - for(unsigned i = 0; i <= size(); i++) { - std::vector<double> temp = (*this)[i].roots(v, d); - for(unsigned j = 0; j < temp.size(); j++) - res.push_back(temp[j] + i); - } - return res; - } - - std::vector<double> - allNearestPoints(Point const& _point, double from, double to) const; - - std::vector<double> - allNearestPoints(Point const& _point) const - { - unsigned int sz = size(); - if ( closed() ) ++sz; - return allNearestPoints(_point, 0, sz); - } - - std::vector<double> - nearestPointPerCurve(Point const& _point) const; - - double nearestPoint(Point const& _point, double from, double to, double *distance_squared = NULL) const; - - double nearestPoint(Point const& _point, double *distance_squared = NULL) const - { - unsigned int sz = size(); - if ( closed() ) ++sz; - return nearestPoint(_point, 0, sz, distance_squared); - } - - void appendPortionTo(Path &p, double f, double t) const; - - Path portion(double f, double t) const { - Path ret; - ret.close(false); - appendPortionTo(ret, f, t); - return ret; - } - Path portion(Interval i) const { return portion(i.min(), i.max()); } - - Path reverse() const { - Path ret(*this); - ret.unshare(); - for ( Sequence::iterator iter = ret.get_curves().begin() ; - iter != ret.get_curves().end()-1 ; ++iter ) + /// Construct a path containing a range of curves. + template <typename Iter> + Path(Iter first, Iter last, bool closed = false, bool stitch = false) + : _curves(new Sequence()) + , _closed(closed) + , _exception_on_stitch(!stitch) { - *iter = boost::shared_ptr<Curve>((*iter)->reverse()); - } - std::reverse(ret.get_curves().begin(), ret.get_curves().end()-1); - ret.final_ = static_cast<ClosingSegment *>(ret.final_->reverse()); - ret.get_curves().back() = boost::shared_ptr<Curve>(ret.final_); - return ret; - } - - void insert(iterator const &pos, - Curve const &curve, Stitching stitching=NO_STITCHING) - { - unshare(); - Sequence::iterator seq_pos(seq_iter(pos)); - Sequence source(1, boost::shared_ptr<Curve>(curve.duplicate())); - if (stitching) stitch(seq_pos, seq_pos, source); - do_update(seq_pos, seq_pos, source.begin(), source.end()); - } - - void insert(iterator const &pos, - const_iterator const &first, - const_iterator const &last, - Stitching stitching=NO_STITCHING) - { - unshare(); - Sequence::iterator seq_pos(seq_iter(pos)); - Sequence source(seq_iter(first), seq_iter(last)); - if (stitching) stitch(seq_pos, seq_pos, source); - do_update(seq_pos, seq_pos, source.begin(), source.end()); - } - - void clear() { - unshare(); - do_update(get_curves().begin(), get_curves().end()-1, - get_curves().begin(), get_curves().begin()); - } - - void erase(iterator const &pos, Stitching stitching=NO_STITCHING) { - unshare(); - Sequence::iterator seq_pos(seq_iter(pos)); - if (stitching) { - Sequence stitched; - stitch(seq_pos, seq_pos+1, stitched); - do_update(seq_pos, seq_pos+1, stitched.begin(), stitched.end()); - } else { - do_update(seq_pos, seq_pos+1, get_curves().begin(), get_curves().begin()); - } - } - - void erase(iterator const &first, - iterator const &last, - Stitching stitching=NO_STITCHING) - { - unshare(); - Sequence::iterator seq_first=seq_iter(first); - Sequence::iterator seq_last=seq_iter(last); - if (stitching) { - Sequence stitched; - stitch(seq_first, seq_last, stitched); - do_update(seq_first, seq_last, stitched.begin(), stitched.end()); - } else { - do_update(seq_first, seq_last, - get_curves().begin(), get_curves().begin()); - } - } - - // erase last segment of path - void erase_last() { - erase(iterator(*this, size()-1)); - } - - void replace(iterator const &replaced, - Curve const &curve, - Stitching stitching=NO_STITCHING) - { - unshare(); - Sequence::iterator seq_replaced(seq_iter(replaced)); - Sequence source(1, boost::shared_ptr<Curve>(curve.duplicate())); - if (stitching) stitch(seq_replaced, seq_replaced+1, source); - do_update(seq_replaced, seq_replaced+1, source.begin(), source.end()); - } - - void replace(iterator const &first_replaced, - iterator const &last_replaced, - Curve const &curve, Stitching stitching=NO_STITCHING) - { - unshare(); - Sequence::iterator seq_first_replaced(seq_iter(first_replaced)); - Sequence::iterator seq_last_replaced(seq_iter(last_replaced)); - Sequence source(1, boost::shared_ptr<Curve>(curve.duplicate())); - if (stitching) stitch(seq_first_replaced, seq_last_replaced, source); - do_update(seq_first_replaced, seq_last_replaced, - source.begin(), source.end()); - } - - void replace(iterator const &replaced, - const_iterator const &first, - const_iterator const &last, - Stitching stitching=NO_STITCHING) - { - unshare(); - Sequence::iterator seq_replaced(seq_iter(replaced)); - Sequence source(seq_iter(first), seq_iter(last)); - if (stitching) stitch(seq_replaced, seq_replaced+1, source); - do_update(seq_replaced, seq_replaced+1, source.begin(), source.end()); - } - - void replace(iterator const &first_replaced, - iterator const &last_replaced, - const_iterator const &first, - const_iterator const &last, - Stitching stitching=NO_STITCHING) - { - unshare(); - Sequence::iterator seq_first_replaced(seq_iter(first_replaced)); - Sequence::iterator seq_last_replaced(seq_iter(last_replaced)); - Sequence source(seq_iter(first), seq_iter(last)); - if (stitching) stitch(seq_first_replaced, seq_last_replaced, source); - do_update(seq_first_replaced, seq_last_replaced, - source.begin(), source.end()); - } - - void start(Point p) { - clear(); - final_->setPoint(0, p); - final_->setPoint(1, p); - } - - Point initialPoint() const { return (*final_)[1]; } - Point finalPoint() const { return (*final_)[0]; } - - void setInitial(Point const& p) - { - if ( empty() ) return; - unshare(); - boost::shared_ptr<Curve> head(front().duplicate()); - head->setInitial(p); - Sequence::iterator replaced = get_curves().begin(); - Sequence source(1, head); - do_update(replaced, replaced + 1, source.begin(), source.end()); - } - - void setFinal(Point const& p) - { - if ( empty() ) return; - unshare(); - boost::shared_ptr<Curve> tail(back().duplicate()); - tail->setFinal(p); - Sequence::iterator replaced = get_curves().end() - 2; - Sequence source(1, tail); - do_update(replaced, replaced + 1, source.begin(), source.end()); - } - - void append(Curve const &curve, Stitching stitching=NO_STITCHING) { - unshare(); - if (stitching) stitchTo(curve.initialPoint()); - do_append(curve.duplicate()); - } - void append(D2<SBasis> const &curve, Stitching stitching=NO_STITCHING) { - unshare(); - if (stitching) stitchTo(Point(curve[X][0][0], curve[Y][0][0])); - do_append(new SBasisCurve(curve)); - } - void append(Path const &other, Stitching stitching=NO_STITCHING) { - insert(end(), other.begin(), other.end(), stitching); - } - - void stitchTo(Point const &p) { - if (!empty() && finalPoint() != p) { - unshare(); - do_append(new StitchSegment(finalPoint(), p)); - } - } - - - /** - * It is important to note that the coordinates passed to appendNew should be finite! - * If one of the coordinates is infinite, 2geom will throw a ContinuityError exception. - */ - - template <typename CurveType, typename A> - void appendNew(A a) { - unshare(); - do_append(new CurveType(finalPoint(), a)); - } - - template <typename CurveType, typename A, typename B> - void appendNew(A a, B b) { - unshare(); - do_append(new CurveType(finalPoint(), a, b)); - } - - template <typename CurveType, typename A, typename B, typename C> - void appendNew(A a, B b, C c) { - unshare(); - do_append(new CurveType(finalPoint(), a, b, c)); - } - - template <typename CurveType, typename A, typename B, typename C, - typename D> - void appendNew(A a, B b, C c, D d) { - unshare(); - do_append(new CurveType(finalPoint(), a, b, c, d)); - } - - template <typename CurveType, typename A, typename B, typename C, - typename D, typename E> - void appendNew(A a, B b, C c, D d, E e) { - unshare(); - do_append(new CurveType(finalPoint(), a, b, c, d, e)); - } - - template <typename CurveType, typename A, typename B, typename C, - typename D, typename E, typename F> - void appendNew(A a, B b, C c, D d, E e, F f) { - unshare(); - do_append(new CurveType(finalPoint(), a, b, c, d, e, f)); - } - - template <typename CurveType, typename A, typename B, typename C, - typename D, typename E, typename F, - typename G> - void appendNew(A a, B b, C c, D d, E e, F f, G g) { - unshare(); - do_append(new CurveType(finalPoint(), a, b, c, d, e, f, g)); - } - - template <typename CurveType, typename A, typename B, typename C, - typename D, typename E, typename F, - typename G, typename H> - void appendNew(A a, B b, C c, D d, E e, F f, G g, H h) { - unshare(); - do_append(new CurveType(finalPoint(), a, b, c, d, e, f, g, h)); - } - - template <typename CurveType, typename A, typename B, typename C, - typename D, typename E, typename F, - typename G, typename H, typename I> - void appendNew(A a, B b, C c, D d, E e, F f, G g, H h, I i) { - unshare(); - do_append(new CurveType(finalPoint(), a, b, c, d, e, f, g, h, i)); - } + for (Iter i = first; i != last; ++i) { + _curves->push_back(i->duplicate()); + } + if (!_curves->empty()) { + _closing_seg = new ClosingSegment(_curves->back().finalPoint(), + _curves->front().initialPoint()); + } else { + _closing_seg = new ClosingSegment(); + } + _curves->push_back(_closing_seg); + } + + /// Create a path from a rectangle. + explicit Path(Rect const &r); + /// Create a path from a convex hull. + explicit Path(ConvexHull const &); + /// Create a path from a circle, using two elliptical arcs. + explicit Path(Circle const &c); + /// Create a path from an ellipse, using two elliptical arcs. + explicit Path(Ellipse const &e); + + virtual ~Path() {} + + // Path &operator=(Path const &other) - use default assignment operator + + /** @brief Swap contents with another path + * @todo Add noexcept specifiers for C++11 */ + void swap(Path &other) throw() { + using std::swap; + swap(other._curves, _curves); + swap(other._closing_seg, _closing_seg); + swap(other._closed, _closed); + swap(other._exception_on_stitch, _exception_on_stitch); + } + /** @brief Swap contents of two paths. + * @relates Path */ + friend inline void swap(Path &a, Path &b) throw() { a.swap(b); } + + /** @brief Access a curve by index */ + Curve const &operator[](size_type i) const { return (*_curves)[i]; } + /** @brief Access a curve by index */ + Curve const &at(size_type i) const { return _curves->at(i); } + + /** @brief Access the first curve in the path. + * Since the curve always contains at least a degenerate closing segment, + * it is always safe to use this method. */ + Curve const &front() const { return _curves->front(); } + /// Alias for front(). + Curve const &initialCurve() const { return _curves->front(); } + /** @brief Access the last curve in the path. */ + Curve const &back() const { return back_default(); } + Curve const &back_open() const { + if (empty()) return _curves->back(); + return (*_curves)[_curves->size() - 2]; + } + Curve const &back_closed() const { + return _closing_seg->isDegenerate() + ? (*_curves)[_curves->size() - 2] + : (*_curves)[_curves->size() - 1]; + } + Curve const &back_default() const { + return _includesClosingSegment() + ? back_closed() + : back_open(); + } + Curve const &finalCurve() const { return back_default(); } + + const_iterator begin() const { return const_iterator(*this, 0); } + const_iterator end() const { return end_default(); } + const_iterator end_default() const { return const_iterator(*this, size_default()); } + const_iterator end_open() const { return const_iterator(*this, size_open()); } + const_iterator end_closed() const { return const_iterator(*this, size_closed()); } + iterator begin() { return iterator(*this, 0); } + iterator end() { return end_default(); } + iterator end_default() { return iterator(*this, size_default()); } + iterator end_open() { return iterator(*this, size_open()); } + iterator end_closed() { return iterator(*this, size_closed()); } + + /// Size without the closing segment, even if the path is closed. + size_type size_open() const { return _curves->size() - 1; } + + /** @brief Size with the closing segment, if it makes a difference. + * If the closing segment is degenerate, i.e. its initial and final points + * are exactly equal, then it is not included in this size. */ + size_type size_closed() const { + return _closing_seg->isDegenerate() ? _curves->size() - 1 : _curves->size(); + } + + /// Natural size of the path. + size_type size_default() const { + return _includesClosingSegment() ? size_closed() : size_open(); + } + /// Natural size of the path. + size_type size() const { return size_default(); } + + size_type max_size() const { return _curves->max_size() - 1; } + + /** @brief Check whether path is empty. + * The path is empty if it contains only the closing segment, which according + * to the continuity invariant must be degenerate. Note that unlike standard + * containers, two empty paths are not necessarily identical, because the + * degenerate closing segment may be at a different point, affecting the operation + * of methods such as appendNew(). */ + bool empty() const { return (_curves->size() == 1); } + + /// Check whether the path is closed. + bool closed() const { return _closed; } + + /** @brief Set whether the path is closed. + * When closing a path where the last segment can be represented as a closing + * segment, the last segment will be removed. When opening a path, the closing + * segment will be erased. This means that closing and then opening a path + * will not always give back the original path. */ + void close(bool closed = true); + + /** @brief Remove all curves from the path. + * The initial and final points of the closing segment are set to (0,0). + * The stitching flag remains unchanged. */ + void clear(); + + /** @brief Get the approximate bounding box. + * The rectangle returned by this method will contain all the curves, but it's not + * guaranteed to be the smallest possible one */ + OptRect boundsFast() const; + + /** @brief Get a tight-fitting bounding box. + * This will return the smallest possible axis-aligned rectangle containing + * all the curves in the path. */ + OptRect boundsExact() const; + + Piecewise<D2<SBasis> > toPwSb() const; + + /// Test paths for exact equality. + bool operator==(Path const &other) const; + + /// Apply a transform to each curve. + template <typename T> + Path &operator*=(T const &tr) { + BOOST_CONCEPT_ASSERT((TransformConcept<T>)); + _unshare(); + for (std::size_t i = 0; i < _curves->size(); ++i) { + (*_curves)[i] *= tr; + } + return *this; + } + + template <typename T> + friend Path operator*(Path const &path, T const &tr) { + BOOST_CONCEPT_ASSERT((TransformConcept<T>)); + Path result(path); + result *= tr; + return result; + } + + /** @brief Get the allowed range of time values. + * @return Values for which pointAt() and valueAt() yield valid results. */ + Interval timeRange() const; + + /** Get the curve at the specified time value. + * @param t Time value + * @param rest Optional storage for the corresponding time value in the curve */ + Curve const &curveAt(Coord t, Coord *rest = NULL) const; + + /// Get the closing segment of the path. + LineSegment const &closingSegment() const { return *_closing_seg; } + + /** @brief Get the point at the specified time value. + * Note that this method has reduced precision with respect to calling pointAt() + * directly on the curve. If you want high precision results, use the version + * that takes a PathTime parameter. + * + * Allowed time values range from zero to the number of curves; you can retrieve + * the allowed range of values with timeRange(). */ + Point pointAt(Coord t) const; + + /// Get one coordinate (X or Y) at the specified time value. + Coord valueAt(Coord t, Dim2 d) const; + + /// Get the curve at the specified path time. + Curve const &curveAt(PathTime const &pos) const; + /// Get the point at the specified path time. + Point pointAt(PathTime const &pos) const; + /// Get one coordinate at the specified path time. + Coord valueAt(PathTime const &pos, Dim2 d) const; + + Point operator()(Coord t) const { return pointAt(t); } + + /// Compute intersections with axis-aligned line. + std::vector<PathTime> roots(Coord v, Dim2 d) const; + + /// Compute intersections with another path. + std::vector<PathIntersection> intersect(Path const &other, Coord precision = EPSILON) const; + + /** @brief Determine the winding number at the specified point. + * + * The winding number is the number of full turns made by a ray that connects the passed + * point and the path's value (i.e. the result of the pointAt() method) as the time increases + * from 0 to the maximum valid value. Positive numbers indicate turns in the direction + * of increasing angles. + * + * Winding numbers are often used as the definition of what is considered "inside" + * the shape. Typically points with either nonzero winding or odd winding are + * considered to be inside the path. */ + int winding(Point const &p) const; + + std::vector<Coord> allNearestTimes(Point const &p, Coord from, Coord to) const; + std::vector<Coord> allNearestTimes(Point const &p) const { + return allNearestTimes(p, 0, size_default()); + } + + PathTime nearestTime(Point const &p, Coord *dist = NULL) const; + std::vector<Coord> nearestTimePerCurve(Point const &p) const; + + void appendPortionTo(Path &p, Coord f, Coord t) const; + + /** @brief Append a subset of this path to another path. + * An extra stitching segment will be inserted if the start point of the portion + * and the final point of the target path do not match exactly. + * The closing segment of the target path will be modified. */ + void appendPortionTo(Path &p, PathTime const &from, PathTime const &to, bool cross_start = false) const { + PathInterval ival(from, to, cross_start, size_closed()); + appendPortionTo(p, ival, boost::none, boost::none); + } + + /** @brief Append a subset of this path to another path. + * This version allows you to explicitly pass a PathInterval. */ + void appendPortionTo(Path &p, PathInterval const &ival) const { + appendPortionTo(p, ival, boost::none, boost::none); + } + + /** @brief Append a subset of this path to another path, specifying endpoints. + * This method is for use in situations where endpoints of the portion segments + * have to be set exactly, for instance when computing Boolean operations. */ + void appendPortionTo(Path &p, PathInterval const &ival, + boost::optional<Point> const &p_from, boost::optional<Point> const &p_to) const; + + Path portion(Coord f, Coord t) const { + Path ret; + ret.close(false); + appendPortionTo(ret, f, t); + return ret; + } + + Path portion(Interval const &i) const { return portion(i.min(), i.max()); } + + /** @brief Get a subset of the current path with full precision. + * When @a from is larger (later in the path) than @a to, the returned portion + * will be reversed. If @a cross_start is true, the portion will be reversed + * and will cross the initial point of the path. Therefore, when @a from is larger + * than @a to and @a cross_start is true, the returned portion will not be reversed, + * but will "wrap around" the end of the path. */ + Path portion(PathTime const &from, PathTime const &to, bool cross_start = false) const { + Path ret; + ret.close(false); + appendPortionTo(ret, from, to, cross_start); + return ret; + } + + /** @brief Get a subset of the current path with full precision. + * This version allows you to explicitly pass a PathInterval. */ + Path portion(PathInterval const &ival) const { + Path ret; + ret.close(false); + appendPortionTo(ret, ival); + return ret; + } + + /** @brief Obtain a reversed version of the current path. + * The final point of the current path will become the initial point + * of the reversed path, unless it is closed and has a non-degenerate + * closing segment. In that case, the new initial point will be the final point + * of the last "real" segment. */ + Path reversed() const; + + void insert(iterator pos, Curve const &curve); + + template <typename Iter> + void insert(iterator pos, Iter first, Iter last) { + _unshare(); + Sequence::iterator seq_pos(seq_iter(pos)); + Sequence source; + for (; first != last; ++first) { + source.push_back(first->duplicate()); + } + do_update(seq_pos, seq_pos, source); + } + + void erase(iterator pos); + void erase(iterator first, iterator last); + + // erase last segment of path + void erase_last() { erase(iterator(*this, size() - 1)); } + + void start(Point const &p); + + /** @brief Get the first point in the path. */ + Point initialPoint() const { return (*_closing_seg)[1]; } + + /** @brief Get the last point in the path. + * If the path is closed, this is always the same as the initial point. */ + Point finalPoint() const { return (*_closing_seg)[_closed ? 1 : 0]; } + + void setInitial(Point const &p) { + _unshare(); + _closed = false; + _curves->front().setInitial(p); + _closing_seg->setFinal(p); + } + void setFinal(Point const &p) { + _unshare(); + _closed = false; + (*_curves)[size_open() - 1].setFinal(p); + _closing_seg->setInitial(p); + } + + /** @brief Add a new curve to the end of the path. + * This inserts the new curve right before the closing segment. + * The path takes ownership of the passed pointer, which should not be freed. */ + void append(Curve *curve) { + _unshare(); + stitchTo(curve->initialPoint()); + do_append(curve); + } + + void append(Curve const &curve) { + _unshare(); + stitchTo(curve.initialPoint()); + do_append(curve.duplicate()); + } + void append(D2<SBasis> const &curve) { + _unshare(); + stitchTo(Point(curve[X][0][0], curve[Y][0][0])); + do_append(new SBasisCurve(curve)); + } + void append(Path const &other) { + replace(end_open(), other.begin(), other.end()); + } + + void replace(iterator replaced, Curve const &curve); + void replace(iterator first, iterator last, Curve const &curve); + void replace(iterator replaced, Path const &path); + void replace(iterator first, iterator last, Path const &path); + + template <typename Iter> + void replace(iterator replaced, Iter first, Iter last) { + replace(replaced, replaced + 1, first, last); + } + + template <typename Iter> + void replace(iterator first_replaced, iterator last_replaced, Iter first, Iter last) { + _unshare(); + Sequence::iterator seq_first_replaced(seq_iter(first_replaced)); + Sequence::iterator seq_last_replaced(seq_iter(last_replaced)); + Sequence source; + for (; first != last; ++first) { + source.push_back(first->duplicate()); + } + do_update(seq_first_replaced, seq_last_replaced, source); + } + + /** @brief Append a new curve to the path. + * + * This family of methods will automaticaly use the current final point of the path + * as the first argument of the new curve's constructor. To call this method, + * you'll need to write e.g.: + * @code + path.template appendNew<CubicBezier>(control1, control2, end_point); + @endcode + * It is important to note that the coordinates passed to appendNew should be finite! + * If one of the coordinates is infinite, 2geom will throw a ContinuityError exception. + */ + template <typename CurveType, typename A> + void appendNew(A a) { + _unshare(); + do_append(new CurveType(finalPoint(), a)); + } + + template <typename CurveType, typename A, typename B> + void appendNew(A a, B b) { + _unshare(); + do_append(new CurveType(finalPoint(), a, b)); + } + + template <typename CurveType, typename A, typename B, typename C> + void appendNew(A a, B b, C c) { + _unshare(); + do_append(new CurveType(finalPoint(), a, b, c)); + } + + template <typename CurveType, typename A, typename B, typename C, typename D> + void appendNew(A a, B b, C c, D d) { + _unshare(); + do_append(new CurveType(finalPoint(), a, b, c, d)); + } + + template <typename CurveType, typename A, typename B, typename C, typename D, typename E> + void appendNew(A a, B b, C c, D d, E e) { + _unshare(); + do_append(new CurveType(finalPoint(), a, b, c, d, e)); + } + + template <typename CurveType, typename A, typename B, typename C, typename D, typename E, typename F> + void appendNew(A a, B b, C c, D d, E e, F f) { + _unshare(); + do_append(new CurveType(finalPoint(), a, b, c, d, e, f)); + } + + template <typename CurveType, typename A, typename B, typename C, typename D, typename E, typename F, typename G> + void appendNew(A a, B b, C c, D d, E e, F f, G g) { + _unshare(); + do_append(new CurveType(finalPoint(), a, b, c, d, e, f, g)); + } + + template <typename CurveType, typename A, typename B, typename C, typename D, typename E, typename F, typename G, + typename H> + void appendNew(A a, B b, C c, D d, E e, F f, G g, H h) { + _unshare(); + do_append(new CurveType(finalPoint(), a, b, c, d, e, f, g, h)); + } + + template <typename CurveType, typename A, typename B, typename C, typename D, typename E, typename F, typename G, + typename H, typename I> + void appendNew(A a, B b, C c, D d, E e, F f, G g, H h, I i) { + _unshare(); + do_append(new CurveType(finalPoint(), a, b, c, d, e, f, g, h, i)); + } + + /** @brief Reduce the closing segment to a point if it's shorter than precision. + * Do this by moving the final point. */ + void snapEnds(Coord precision = EPSILON); + + /// Append a stitching segment ending at the specified point. + void stitchTo(Point const &p); + + /** @brief Verify the continuity invariant. + * If the path is not contiguous, this will throw a CountinuityError. */ + void checkContinuity() const; + + /** @brief Enable or disable the throwing of exceptions when stitching discontinuities. + * Normally stitching will cause exceptions, but when you are working with unsanitized + * curve data, you can disable these exceptions. */ + void setStitching(bool x) { + _exception_on_stitch = !x; + } private: - static Sequence::iterator seq_iter(iterator const &iter) { - return iter.path->get_curves().begin() + iter.index; - } - static Sequence::const_iterator seq_iter(const_iterator const &iter) { - return iter.path->get_curves().begin() + iter.index; - } - - Sequence &get_curves() { return *curves_; } - Sequence const &get_curves() const { return *curves_; } - - void unshare() { - if (!curves_.unique()) { - curves_ = boost::shared_ptr<Sequence>(new Sequence(*curves_)); - } - if (!get_curves().back().unique()) { - final_ = static_cast<ClosingSegment *>(final_->duplicate()); - get_curves().back() = boost::shared_ptr<Curve>(final_); - } - } - - void stitch(Sequence::iterator first_replaced, - Sequence::iterator last_replaced, - Sequence &sequence); - - void do_update(Sequence::iterator first_replaced, - Sequence::iterator last_replaced, - Sequence::iterator first, - Sequence::iterator last); - - // n.b. takes ownership of curve object - void do_append(Curve *curve); - - void check_continuity(Sequence::iterator first_replaced, - Sequence::iterator last_replaced, - Sequence::iterator first, - Sequence::iterator last); - - boost::shared_ptr<Sequence> curves_; - ClosingSegment *final_; - bool closed_; -}; // end class Path - -inline static Piecewise<D2<SBasis> > paths_to_pw(std::vector<Path> paths) { - Piecewise<D2<SBasis> > ret = paths[0].toPwSb(); - for(unsigned i = 1; i < paths.size(); i++) { - ret.concat(paths[i].toPwSb()); - } - return ret; -} + static Sequence::iterator seq_iter(iterator const &iter) { + return iter.path->_curves->begin() + iter.index; + } + static Sequence::const_iterator seq_iter(const_iterator const &iter) { + return iter.path->_curves->begin() + iter.index; + } -inline -Coord nearest_point(Point const& p, Path const& c) -{ - return c.nearestPoint(p); + // whether the closing segment is part of the path + bool _includesClosingSegment() const { + return _closed && !_closing_seg->isDegenerate(); + } + void _unshare() { + if (!_curves.unique()) { + _curves.reset(new Sequence(*_curves)); + _closing_seg = static_cast<ClosingSegment*>(&_curves->back()); + } + } + PathTime _factorTime(Coord t) const; + + void stitch(Sequence::iterator first_replaced, Sequence::iterator last_replaced, Sequence &sequence); + void do_update(Sequence::iterator first, Sequence::iterator last, Sequence &source); + + // n.b. takes ownership of curve object + void do_append(Curve *curve); + + boost::shared_ptr<Sequence> _curves; + ClosingSegment *_closing_seg; + bool _closed; + bool _exception_on_stitch; +}; // end class Path + +Piecewise<D2<SBasis> > paths_to_pw(PathVector const &paths); + +inline Coord nearest_time(Point const &p, Path const &c) { + PathTime pt = c.nearestTime(p); + return pt.curve_index + pt.t; } -} // end namespace Geom +std::ostream &operator<<(std::ostream &out, Path const &path); + +} // end namespace Geom #endif // LIB2GEOM_SEEN_PATH_H diff --git a/src/2geom/pathvector.cpp b/src/2geom/pathvector.cpp index fc0ad75c4..bff201c71 100644 --- a/src/2geom/pathvector.cpp +++ b/src/2geom/pathvector.cpp @@ -1,12 +1,11 @@ -/* - * PathVector - std::vector containing Geom::Path - * This file provides a set of operations that can be performed on PathVector, - * e.g. an affine transform. - * +/** @file + * @brief PathVector - a sequence of subpaths + *//* * Authors: - * Johan Engelen <goejendaagh@zonnet.nl> + * Johan Engelen <goejendaagh@zonnet.nl> + * Krzysztof Kosiński <tweenk.pl@gmail.com> * - * Copyright 2008 authors + * Copyright 2008-2014 Authors * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public @@ -32,113 +31,205 @@ * the specific language governing rights and limitations. */ -#ifndef SEEN_GEOM_PATHVECTOR_CPP -#define SEEN_GEOM_PATHVECTOR_CPP - -#include <2geom/pathvector.h> - -#include <2geom/path.h> #include <2geom/affine.h> +#include <2geom/path.h> +#include <2geom/pathvector.h> +#include <2geom/svg-path-writer.h> namespace Geom { -// TODO: see which of these functions can be inlined for optimization +//PathVector &PathVector::operator+=(PathVector const &other); + +PathVector::size_type PathVector::curveCount() const +{ + size_type n = 0; + for (const_iterator it = begin(); it != end(); ++it) { + n += it->size_default(); + } + return n; +} + +void PathVector::reverse(bool reverse_paths) +{ + if (reverse_paths) { + std::reverse(begin(), end()); + } + for (iterator i = begin(); i != end(); ++i) { + *i = i->reversed(); + } +} -/** - * Reverses all Paths and the order of paths in the vector as well - **/ -PathVector reverse_paths_and_order (PathVector const & path_in) +PathVector PathVector::reversed(bool reverse_paths) const { - PathVector path_out; - for (PathVector::const_reverse_iterator it = path_in.rbegin(); it != path_in.rend(); ++it) { - path_out.push_back( (*it).reverse() ); + PathVector ret; + for (const_iterator i = begin(); i != end(); ++i) { + ret.push_back(i->reversed()); + } + if (reverse_paths) { + std::reverse(ret.begin(), ret.end()); } - return path_out; + return ret; } -OptRect bounds_fast( PathVector const& pv ) +Path &PathVector::pathAt(Coord t, Coord *rest) +{ + return const_cast<Path &>(static_cast<PathVector const*>(this)->pathAt(t, rest)); +} +Path const &PathVector::pathAt(Coord t, Coord *rest) const +{ + PathVectorTime pos = _factorTime(t); + if (rest) { + *rest = Coord(pos.curve_index) + pos.t; + } + return at(pos.path_index); +} +Curve const &PathVector::curveAt(Coord t, Coord *rest) const +{ + PathVectorTime pos = _factorTime(t); + if (rest) { + *rest = pos.t; + } + return at(pos.path_index).at(pos.curve_index); +} +Coord PathVector::valueAt(Coord t, Dim2 d) const +{ + PathVectorTime pos = _factorTime(t); + return at(pos.path_index).at(pos.curve_index).valueAt(pos.t, d); +} +Point PathVector::pointAt(Coord t) const +{ + PathVectorTime pos = _factorTime(t); + return at(pos.path_index).at(pos.curve_index).pointAt(pos.t); +} + +OptRect PathVector::boundsFast() const { - typedef PathVector::const_iterator const_iterator; - OptRect bound; - if (pv.empty()) return bound; - - bound = (pv.begin())->boundsFast(); - for (const_iterator it = ++(pv.begin()); it != pv.end(); ++it) - { + if (empty()) return bound; + + bound = front().boundsFast(); + for (const_iterator it = ++begin(); it != end(); ++it) { bound.unionWith(it->boundsFast()); } return bound; } -OptRect bounds_exact( PathVector const& pv ) +OptRect PathVector::boundsExact() const { - typedef PathVector::const_iterator const_iterator; - OptRect bound; - if (pv.empty()) return bound; - - bound = (pv.begin())->boundsExact(); - for (const_iterator it = ++(pv.begin()); it != pv.end(); ++it) - { + if (empty()) return bound; + + bound = front().boundsExact(); + for (const_iterator it = ++begin(); it != end(); ++it) { bound.unionWith(it->boundsExact()); } return bound; } -/* Note: undefined for empty pathvectors or pathvectors with empty paths. - * */ -boost::optional<PathVectorPosition> nearestPoint(PathVector const & path_in, Point const& _point, double *distance_squared) +void PathVector::snapEnds(Coord precision) { - boost::optional<PathVectorPosition> retval; + for (std::size_t i = 0; i < size(); ++i) { + (*this)[i].snapEnds(precision); + } +} - double mindsq = infinity(); - unsigned int i = 0; - for (Geom::PathVector::const_iterator pit = path_in.begin(); pit != path_in.end(); ++pit) { - double dsq; - double t = pit->nearestPoint(_point, &dsq); - //std::cout << t << "," << dsq << std::endl; - if (dsq < mindsq) { - mindsq = dsq; - retval = PathVectorPosition(i, t); +std::vector<PVIntersection> PathVector::intersect(PathVector const &other, Coord precision) const +{ + typedef PathVectorTime PVPos; + std::vector<PVIntersection> result; + for (std::size_t i = 0; i < size(); ++i) { + for (std::size_t j = 0; j < other.size(); ++j) { + std::vector<PathIntersection> xs = (*this)[i].intersect(other[j], precision); + for (std::size_t k = 0; k < xs.size(); ++k) { + PVIntersection pvx(PVPos(i, xs[k].first), PVPos(j, xs[k].second), xs[k].point()); + result.push_back(pvx); + } } + } + return result; +} - ++i; +int PathVector::winding(Point const &p) const +{ + int wind = 0; + for (const_iterator i = begin(); i != end(); ++i) { + wind += i->winding(p); } + return wind; +} - if (distance_squared) { - *distance_squared = mindsq; +boost::optional<PathVectorTime> PathVector::nearestTime(Point const &p, Coord *dist) const +{ + boost::optional<PathVectorTime> retval; + + Coord mindist = infinity(); + for (size_type i = 0; i < size(); ++i) { + Coord d; + PathTime pos = (*this)[i].nearestTime(p, &d); + if (d < mindist) { + mindist = d; + retval = PathVectorTime(i, pos.curve_index, pos.t); + } + } + + if (dist) { + *dist = mindist; } return retval; } -std::vector<PathVectorPosition> allNearestPoints(PathVector const & path_in, Point const& _point, double *distance_squared) +std::vector<PathVectorTime> PathVector::allNearestTimes(Point const &p, Coord *dist) const { - std::vector<PathVectorPosition> retval; - - double mindsq = infinity(); - unsigned int i = 0; - for (Geom::PathVector::const_iterator pit = path_in.begin(); pit != path_in.end(); ++pit) { - double dsq; - double t = pit->nearestPoint(_point, &dsq); - if (dsq < mindsq) { - mindsq = dsq; - retval.push_back(PathVectorPosition(i, t)); + std::vector<PathVectorTime> retval; + + Coord mindist = infinity(); + for (size_type i = 0; i < size(); ++i) { + Coord d; + PathTime pos = (*this)[i].nearestTime(p, &d); + if (d < mindist) { + mindist = d; + retval.clear(); + } + if (d <= mindist) { + retval.push_back(PathVectorTime(i, pos.curve_index, pos.t)); } - - ++i; } - if (distance_squared) { - *distance_squared = mindsq; + if (dist) { + *dist = mindist; } return retval; +} +PathVectorTime PathVector::_factorTime(Coord t) const +{ + PathVectorTime ret; + Coord rest = 0; + ret.t = modf(t, &rest); + ret.curve_index = rest; + for (; ret.path_index < size(); ++ret.path_index) { + unsigned s = _data.at(ret.path_index).size_default(); + if (s > ret.curve_index) break; + // special case for the last point + if (s == ret.curve_index && ret.path_index + 1 == size()) { + --ret.curve_index; + ret.t = 1; + break; + } + ret.curve_index -= s; + } + return ret; } -} // namespace Geom +std::ostream &operator<<(std::ostream &out, PathVector const &pv) +{ + SVGPathWriter wr; + wr.feed(pv); + out << wr.str(); + return out; +} -#endif // SEEN_GEOM_PATHVECTOR_CPP +} // namespace Geom /* Local Variables: diff --git a/src/2geom/pathvector.h b/src/2geom/pathvector.h index e875e915f..108f2aa05 100644 --- a/src/2geom/pathvector.h +++ b/src/2geom/pathvector.h @@ -1,13 +1,11 @@ -/** - * \file - * \brief PathVector - std::vector containing Geom::Path. - * This file provides a set of operations that can be performed on PathVector, - * e.g. an affine transform. +/** @file + * @brief PathVector - a sequence of subpaths *//* * Authors: - * Johan Engelen <goejendaagh@zonnet.nl> + * Johan Engelen <j.b.c.engelen@alumnus.utwente.nl> + * Krzysztof Kosiński <tweenk.pl@gmail.com> * - * Copyright 2008 authors + * Copyright 2008-2014 authors * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public @@ -36,93 +34,254 @@ #ifndef LIB2GEOM_SEEN_PATHVECTOR_H #define LIB2GEOM_SEEN_PATHVECTOR_H +#include <boost/concept/requires.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/range/algorithm/equal.hpp> #include <2geom/forward.h> #include <2geom/path.h> #include <2geom/transforms.h> namespace Geom { -typedef std::vector<Geom::Path> PathVector; +/** @brief Generalized time value in the path vector. + * + * This class exists because mapping the range of multiple curves onto the same interval + * as the curve index, we lose some precision. For instance, a path with 16 curves will + * have 4 bits less precision than a path with 1 curve. If you need high precision results + * in long paths, use this class and related methods instead of the standard methods + * pointAt(), nearestTime() and so on. + * + * @ingroup Paths */ +struct PathVectorTime + : public PathTime + , boost::totally_ordered<PathVectorTime> +{ + size_type path_index; ///< Index of the path in the vector -/* general path transformation: */ -inline -void operator*= (PathVector & path_in, Affine const &m) { - for(PathVector::iterator it = path_in.begin(); it != path_in.end(); ++it) { - (*it) *= m; - } -} -inline -PathVector operator*(PathVector const & path_in, Affine const &m) { - PathVector ret(path_in); - ret *= m; - return ret; -} + PathVectorTime() : PathTime(0, 0), path_index(0) {} + PathVectorTime(size_type _i, size_type _c, Coord _t) + : PathTime(_c, _t), path_index(_i) {} + PathVectorTime(size_type _i, PathTime const &pos) + : PathTime(pos), path_index(_i) {} -/* specific path transformations: Translation: - * This makes it possible to make optimized implementations for Translate transforms */ -inline -void operator*= (PathVector & path_in, Translate const &m) { - for(PathVector::iterator it = path_in.begin(); it != path_in.end(); ++it) { - (*it) *= m; + bool operator<(PathVectorTime const &other) const { + if (path_index < other.path_index) return true; + if (path_index == other.path_index) { + return static_cast<PathTime const &>(*this) < static_cast<PathTime const &>(other); + } + return false; + } + bool operator==(PathVectorTime const &other) const { + return path_index == other.path_index + && static_cast<PathTime const &>(*this) == static_cast<PathTime const &>(other); } -} -inline -PathVector operator*(PathVector const & path_in, Translate const &m) { - PathVector ret(path_in); - ret *= m; - return ret; -} -/* user friendly approach to Translate transforms: just add an offset Point to the whole path */ -inline -void operator+=(PathVector &path_in, Point const &p) { - for(PathVector::iterator it = path_in.begin(); it != path_in.end(); ++it) { - (*it) *= Translate(p); + PathTime const &asPathTime() const { + return *static_cast<PathTime const *>(this); } -} -inline -PathVector operator+(PathVector const &path_in, Point const &p) { - PathVector ret(path_in); - ret *= Translate(p); - return ret; -} +}; -inline -Geom::Point initialPoint(PathVector const &path_in) -{ - return path_in.front().initialPoint(); +inline std::ostream &operator<<(std::ostream &os, PathVectorTime const &pvt) { + os << pvt.path_index << ": " << pvt.asPathTime(); + return os; } -inline -Geom::Point finalPoint(PathVector const &path_in) +typedef Intersection<PathVectorTime> PathVectorIntersection; +typedef PathVectorIntersection PVIntersection; ///< Alias to save typing + +template <> +struct ShapeTraits<PathVector> { + typedef PathVectorTime TimeType; + //typedef PathVectorInterval IntervalType; + typedef PathVector AffineClosureType; + typedef PathVectorIntersection IntersectionType; +}; + +/** @brief Sequence of subpaths. + * + * This class corresponds to the SVG notion of a path: + * a sequence of any number of open or closed contiguous subpaths. + * Unlike Path, this class is closed under boolean operations. + * + * If you want to represent an arbitrary shape, this is the best class to use. + * Shapes with a boundary that is composed of only a single contiguous + * component can be represented with Path instead. + * + * @ingroup Paths + */ +class PathVector + : MultipliableNoncommutative< PathVector, Affine + , MultipliableNoncommutative< PathVector, Translate + , MultipliableNoncommutative< PathVector, Scale + , MultipliableNoncommutative< PathVector, Rotate + , MultipliableNoncommutative< PathVector, HShear + , MultipliableNoncommutative< PathVector, VShear + , MultipliableNoncommutative< PathVector, Zoom + , boost::equality_comparable< PathVector + > > > > > > > > { - return path_in.back().finalPoint(); -} + typedef std::vector<Path> Sequence; +public: + typedef PathVectorTime Position; + typedef Sequence::iterator iterator; + typedef Sequence::const_iterator const_iterator; + typedef Sequence::size_type size_type; + typedef Path value_type; + typedef Path &reference; + typedef Path const &const_reference; + typedef Path *pointer; + typedef std::ptrdiff_t difference_type; -PathVector reverse_paths_and_order (PathVector const & path_in); - -OptRect bounds_fast( PathVector const & pv ); -OptRect bounds_exact( PathVector const & pv ); - -struct PathVectorPosition { - // pathvector[path_nr].pointAt(t) is the position - unsigned int path_nr; - double t; - PathVectorPosition() : - path_nr(0), - t(0) - {} - PathVectorPosition(unsigned int path_nr, - double t) : path_nr(path_nr), t(t) {} + PathVector() {} + PathVector(Path const &p) + : _data(1, p) + {} + template <typename InputIter> + PathVector(InputIter first, InputIter last) + : _data(first, last) + {} + + /// Check whether the vector contains any paths. + bool empty() const { return _data.empty(); } + /// Get the number of paths in the vector. + size_type size() const { return _data.size(); } + /// Get the total number of curves in the vector. + size_type curveCount() const; + + iterator begin() { return _data.begin(); } + iterator end() { return _data.end(); } + const_iterator begin() const { return _data.begin(); } + const_iterator end() const { return _data.end(); } + Path &operator[](size_type index) { + return _data[index]; + } + Path const &operator[](size_type index) const { + return _data[index]; + } + Path &at(size_type index) { + return _data.at(index); + } + Path const &at(size_type index) const { + return _data.at(index); + } + Path &front() { return _data.front(); } + Path const &front() const { return _data.front(); } + Path &back() { return _data.back(); } + Path const &back() const { return _data.back(); } + /// Append a path at the end. + void push_back(Path const &path) { + _data.push_back(path); + } + /// Remove the last path. + void pop_back() { + _data.pop_back(); + } + iterator insert(iterator pos, Path const &p) { + return _data.insert(pos, p); + } + template <typename InputIter> + void insert(iterator out, InputIter first, InputIter last) { + _data.insert(out, first, last); + } + /// Remove a path from the vector. + iterator erase(iterator i) { + return _data.erase(i); + } + /// Remove a range of paths from the vector. + iterator erase(iterator first, iterator last) { + return _data.erase(first, last); + } + /// Remove all paths from the vector. + void clear() { _data.clear(); } + /** @brief Change the number of paths. + * If the vector size increases, it is passed with paths that contain only + * a degenerate closing segment at (0,0). */ + void resize(size_type n) { _data.resize(n); } + /** @brief Reverse the direction of paths in the vector. + * @param reverse_paths If this is true, the order of paths is reversed as well; + * otherwise each path is reversed, but their order in the + * PathVector stays the same */ + void reverse(bool reverse_paths = true); + /** @brief Get a new vector with reversed direction of paths. + * @param reverse_paths If this is true, the order of paths is reversed as well; + * otherwise each path is reversed, but their order in the + * PathVector stays the same */ + PathVector reversed(bool reverse_paths = true) const; + + /// Get the range of allowed time values. + Interval timeRange() const { + Interval ret(0, curveCount()); return ret; + } + /** @brief Get the first point in the first path of the vector. + * This method will throw an exception if the vector doesn't contain any paths. */ + Point initialPoint() const { + return _data.front().initialPoint(); + } + /** @brief Get the last point in the last path of the vector. + * This method will throw an exception if the vector doesn't contain any paths. */ + Point finalPoint() const { + return _data.back().finalPoint(); + } + Path &pathAt(Coord t, Coord *rest = NULL); + Path const &pathAt(Coord t, Coord *rest = NULL) const; + Curve const &curveAt(Coord t, Coord *rest = NULL) const; + Coord valueAt(Coord t, Dim2 d) const; + Point pointAt(Coord t) const; + + Path &pathAt(PathVectorTime const &pos) { + return const_cast<Path &>(static_cast<PathVector const*>(this)->pathAt(pos)); + } + Path const &pathAt(PathVectorTime const &pos) const { + return at(pos.path_index); + } + Curve const &curveAt(PathVectorTime const &pos) const { + return at(pos.path_index).at(pos.curve_index); + } + Point pointAt(PathVectorTime const &pos) const { + return at(pos.path_index).at(pos.curve_index).pointAt(pos.t); + } + Coord valueAt(PathVectorTime const &pos, Dim2 d) const { + return at(pos.path_index).at(pos.curve_index).valueAt(pos.t, d); + } + + OptRect boundsFast() const; + OptRect boundsExact() const; + + template <typename T> + BOOST_CONCEPT_REQUIRES(((TransformConcept<T>)), (PathVector &)) + operator*=(T const &t) { + if (empty()) return *this; + for (iterator i = begin(); i != end(); ++i) { + *i *= t; + } + return *this; + } + + bool operator==(PathVector const &other) const { + return boost::range::equal(_data, other._data); + } + + void snapEnds(Coord precision = EPSILON); + + std::vector<PVIntersection> intersect(PathVector const &other, Coord precision = EPSILON) const; + + /** @brief Determine the winding number at the specified point. + * This is simply the sum of winding numbers for constituent paths. */ + int winding(Point const &p) const; + + boost::optional<PathVectorTime> nearestTime(Point const &p, Coord *dist = NULL) const; + std::vector<PathVectorTime> allNearestTimes(Point const &p, Coord *dist = NULL) const; + +private: + PathVectorTime _factorTime(Coord t) const; + + Sequence _data; }; -boost::optional<PathVectorPosition> nearestPoint(PathVector const & path_in, Point const& _point, double *distance_squared = NULL); -std::vector<PathVectorPosition> allNearestPoints(PathVector const & path_in, Point const& _point, double *distance_squared = NULL); +inline OptRect bounds_fast(PathVector const &pv) { return pv.boundsFast(); } +inline OptRect bounds_exact(PathVector const &pv) { return pv.boundsExact(); } -inline -Point pointAt(PathVector const & path_in, PathVectorPosition const &pvp) { - return path_in[pvp.path_nr].pointAt(pvp.t); -} +std::ostream &operator<<(std::ostream &out, PathVector const &pv); } // end namespace Geom diff --git a/src/2geom/piecewise.h b/src/2geom/piecewise.h index ab8417254..a5a65a9fe 100644 --- a/src/2geom/piecewise.h +++ b/src/2geom/piecewise.h @@ -1,7 +1,6 @@ -/** - * \file - * \brief Piecewise function class - * +/** @file + * @brief Piecewise function class + *//* * Copyright 2007 Michael Sloan <mgsloan@gmail.com> * * This library is free software; you can redistribute it and/or @@ -29,8 +28,8 @@ * */ -#ifndef SEEN_GEOM_PW_SB_H -#define SEEN_GEOM_PW_SB_H +#ifndef LIB2GEOM_SEEN_PIECEWISE_H +#define LIB2GEOM_SEEN_PIECEWISE_H #include <vector> #include <map> @@ -43,12 +42,14 @@ namespace Geom { /** - * %Piecewise function class. + * @brief Function defined as discrete pieces. + * * The Piecewise class manages a sequence of elements of a type as segments and * the ’cuts’ between them. These cuts are time values which separate the pieces. * This function representation allows for more interesting functions, as it provides * a viable output for operations such as inversion, which may require multiple * SBasis to properly invert the original. + * * As for technical details, while the actual SBasis segments begin on the first * cut and end on the last, the function is defined throughout all inputs by ex- * tending the first and last segments. The exact switching between segments is @@ -63,6 +64,8 @@ namespace Geom { * s_n,& c_n <= t * \end{array}\right. * \f] + * + * @ingroup Fragments */ template <typename T> class Piecewise { @@ -933,7 +936,7 @@ Piecewise<T> lerp(double t, Piecewise<T> const &a, Piecewise<T> b) { } } -#endif //SEEN_GEOM_PW_SB_H +#endif //LIB2GEOM_SEEN_PIECEWISE_H /* Local Variables: mode:c++ diff --git a/src/2geom/point-ops.h b/src/2geom/point-ops.h deleted file mode 100644 index 6f5eab56b..000000000 --- a/src/2geom/point-ops.h +++ /dev/null @@ -1,25 +0,0 @@ -//[[[cog -import operators - -setContext("Point", "Matrix", "Point") -make({'*':'*='}, {'/':'/='}) -apsnd({'*':'/'}, "b.inverse()") - -setContext("Point", "double", "Point") -make({'*=':'*'}, {'/=':'/'}, {'*':'*'}, {'*':'/'}) - -setContext("Point", "Point", "bool") -make({'==':'!='}) - -setContext("Point", "Point", "Point") -make({'+=':'+', '-=':'-'}) -]]] - -************** -GENERATED CODE -************** -If you wish to modify, move function out of generation region and remove the -cause of its generation. -*/ - -//[[[end]]] diff --git a/src/2geom/point.cpp b/src/2geom/point.cpp index b0b00b5da..bcdbab429 100644 --- a/src/2geom/point.cpp +++ b/src/2geom/point.cpp @@ -35,6 +35,7 @@ #include <assert.h> #include <math.h> +#include <2geom/coord.h> #include <2geom/point.h> #include <2geom/transforms.h> @@ -146,8 +147,8 @@ bool is_zero(Point const &p) { /** @brief True if the point has a length near 1. The are_near() function is used. * @relates Point */ -bool is_unit_vector(Point const &p) { - return are_near(L2(p), 1.0); +bool is_unit_vector(Point const &p, Coord eps) { + return are_near(L2(p), 1.0, eps); } /** @brief Return the angle between the point and the +X axis. * @return Angle in \f$(-\pi, \pi]\f$. @@ -161,7 +162,7 @@ Coord atan2(Point const &p) { * @return Angle in \f$(-\pi, \pi]\f$. * @relates Point */ Coord angle_between(Point const &a, Point const &b) { - return std::atan2(cross(b,a), dot(b,a)); + return std::atan2(cross(a,b), dot(a,b)); } /** @brief Create a normalized version of a point. @@ -230,6 +231,13 @@ Point constrain_angle(Point const &A, Point const &B, unsigned int n, Point cons return A + dir * Rotate(k * 2.0 * M_PI / (double)n) * L2(diff); } +std::ostream &operator<<(std::ostream &out, const Geom::Point &p) +{ + out << "(" << format_coord_nice(p[X]) << ", " + << format_coord_nice(p[Y]) << ")"; + return out; +} + } // end namespace Geom /* diff --git a/src/2geom/point.h b/src/2geom/point.h index 23dcfd54f..a66e64647 100644 --- a/src/2geom/point.h +++ b/src/2geom/point.h @@ -1,6 +1,5 @@ -/** - * \file - * \brief Cartesian point / 2D vector and related operations +/** @file + * @brief Cartesian point / 2D vector and related operations *//* * Authors: * Michael G. Sloan <mgsloan@gmail.com> @@ -33,8 +32,8 @@ * the specific language governing rights and limitations. */ -#ifndef SEEN_Geom_POINT_H -#define SEEN_Geom_POINT_H +#ifndef LIB2GEOM_SEEN_POINT_H +#define LIB2GEOM_SEEN_POINT_H #include "config.h" #include <iostream> @@ -59,10 +58,14 @@ class Point , MultipliableNoncommutative< Point, HShear , MultipliableNoncommutative< Point, VShear , MultipliableNoncommutative< Point, Zoom - > > > > > > > > > > // this uses chaining so it looks weird, but works + > > > > > > > > > > // base class chaining, see documentation for Boost.Operator { Coord _pt[2]; public: + typedef Coord D1Value; + typedef Coord &D1Reference; + typedef Coord const &D1ConstReference; + /// @name Create points /// @{ /** Construct a point on the origin. */ @@ -116,6 +119,11 @@ public: * @return Length of the vector from origin to this point */ Coord length() const { return hypot(_pt[0], _pt[1]); } void normalize(); + Point normalized() const { + Point ret(*this); + ret.normalize(); + return ret; + } /** @brief Return a point like this point but rotated -90 degrees. * If the y axis grows downwards and the x axis grows to the @@ -223,40 +231,73 @@ public: } /// @} - /** @brief Lexicographical ordering functor. */ - template <Dim2 d> struct LexOrder; + /** @brief Lexicographical ordering functor. + * @param d The dimension with higher significance */ + template <Dim2 DIM> struct LexLess; + template <Dim2 DIM> struct LexGreater; + //template <Dim2 DIM, typename First = std::less<Coord>, typename Second = std::less<Coord> > LexOrder; /** @brief Lexicographical ordering functor with runtime dimension. */ - class LexOrderRt { - public: - LexOrderRt(Dim2 d) : dim(d) {} - inline bool operator()(Point const &a, Point const &b); + struct LexLessRt { + LexLessRt(Dim2 d) : dim(d) {} + inline bool operator()(Point const &a, Point const &b) const; private: Dim2 dim; }; - - friend inline std::ostream &operator<< (std::ostream &out_file, const Geom::Point &in_pnt); + struct LexGreaterRt { + LexGreaterRt(Dim2 d) : dim(d) {} + inline bool operator()(Point const &a, Point const &b) const; + private: + Dim2 dim; + }; + //template <typename First = std::less<Coord>, typename Second = std::less<Coord> > LexOrder }; /** @brief Output operator for points. * Prints out the coordinates. * @relates Point */ -inline std::ostream &operator<< (std::ostream &out_file, const Geom::Point &in_pnt) { - out_file << "X: " << in_pnt[X] << " Y: " << in_pnt[Y]; - return out_file; -} - -template<> struct Point::LexOrder<X> { - bool operator()(Point const &a, Point const &b) { +std::ostream &operator<<(std::ostream &out, const Geom::Point &p); + +template<> struct Point::LexLess<X> { + typedef std::less<Coord> Primary; + typedef std::less<Coord> Secondary; + typedef std::less<Coord> XOrder; + typedef std::less<Coord> YOrder; + bool operator()(Point const &a, Point const &b) const { return a[X] < b[X] || (a[X] == b[X] && a[Y] < b[Y]); } }; -template<> struct Point::LexOrder<Y> { - bool operator()(Point const &a, Point const &b) { +template<> struct Point::LexLess<Y> { + typedef std::less<Coord> Primary; + typedef std::less<Coord> Secondary; + typedef std::less<Coord> XOrder; + typedef std::less<Coord> YOrder; + bool operator()(Point const &a, Point const &b) const { return a[Y] < b[Y] || (a[Y] == b[Y] && a[X] < b[X]); } }; -inline bool Point::LexOrderRt::operator()(Point const &a, Point const &b) { - return dim ? Point::LexOrder<Y>()(a, b) : Point::LexOrder<X>()(a, b); +template<> struct Point::LexGreater<X> { + typedef std::greater<Coord> Primary; + typedef std::greater<Coord> Secondary; + typedef std::greater<Coord> XOrder; + typedef std::greater<Coord> YOrder; + bool operator()(Point const &a, Point const &b) const { + return a[X] > b[X] || (a[X] == b[X] && a[Y] > b[Y]); + } +}; +template<> struct Point::LexGreater<Y> { + typedef std::greater<Coord> Primary; + typedef std::greater<Coord> Secondary; + typedef std::greater<Coord> XOrder; + typedef std::greater<Coord> YOrder; + bool operator()(Point const &a, Point const &b) const { + return a[Y] > b[Y] || (a[Y] == b[Y] && a[X] > b[X]); + } +}; +inline bool Point::LexLessRt::operator()(Point const &a, Point const &b) const { + return dim ? Point::LexLess<Y>()(a, b) : Point::LexLess<X>()(a, b); +} +inline bool Point::LexGreaterRt::operator()(Point const &a, Point const &b) const { + return dim ? Point::LexGreater<Y>()(a, b) : Point::LexGreater<X>()(a, b); } /** @brief Compute the second (Euclidean) norm of @a p. @@ -265,8 +306,7 @@ inline bool Point::LexOrderRt::operator()(Point const &a, Point const &b) { * in a <code>double</code>. * @return \f$\sqrt{p_X^2 + p_Y^2}\f$ * @relates Point */ -inline Coord L2(Point const &p) -{ +inline Coord L2(Point const &p) { return p.length(); } @@ -274,28 +314,10 @@ inline Coord L2(Point const &p) * Warning: this can overflow where L2 won't. * @return \f$p_X^2 + p_Y^2\f$ * @relates Point */ -inline Coord L2sq(Point const &p) -{ +inline Coord L2sq(Point const &p) { return p[0]*p[0] + p[1]*p[1]; } -//IMPL: NearConcept -/** @brief Nearness predicate for points. - * True if neither coordinate of @a a is further than @a eps from the corresponding - * coordinate of @a b. - * @relates Point */ -inline bool are_near(Point const &a, Point const &b, double const eps=EPSILON) -{ - return ( are_near(a[X],b[X],eps) && are_near(a[Y],b[Y],eps) ); -} - -/** @brief Return a point halfway between the specified ones. - * @relates Point */ -inline Point middle_point(Point const& P1, Point const& P2) -{ - return (P1 + P2) / 2; -} - /** @brief Returns p * Geom::rotate_degrees(90), but more efficient. * * Angle direction in 2Geom: If you use the traditional mathematics convention that y @@ -305,8 +327,7 @@ inline Point middle_point(Point const& P1, Point const& P2) * * There is no function to rotate by -90 degrees: use -rot90(p) instead. * @relates Point */ -inline Point rot90(Point const &p) -{ +inline Point rot90(Point const &p) { return Point(-p[Y], p[X]); } @@ -317,9 +338,14 @@ inline Point rot90(Point const &p) * @return Point on a line between a and b. The ratio of its distance from a * and the distance between a and b will be equal to t. * @relates Point */ -inline Point lerp(double const t, Point const &a, Point const &b) -{ - return (a * (1 - t) + b * t); +inline Point lerp(Coord t, Point const &a, Point const &b) { + return (1 - t) * a + t * b; +} + +/** @brief Return a point halfway between the specified ones. + * @relates Point */ +inline Point middle_point(Point const &p1, Point const &p2) { + return lerp(0.5, p1, p2); } /** @brief Compute the dot product of a and b. @@ -328,45 +354,62 @@ inline Point lerp(double const t, Point const &a, Point const &b) * and the sign depends on whether they point in the same direction (+) or opposite ones (-). * @return \f$a \cdot b = a_X b_X + a_Y b_Y\f$. * @relates Point */ -inline Coord dot(Point const &a, Point const &b) -{ - return a[0] * b[0] + a[1] * b[1]; +inline Coord dot(Point const &a, Point const &b) { + return a[X] * b[X] + a[Y] * b[Y]; } /** @brief Compute the 2D cross product. - * Defined as dot(a, b.cw()). This means it will be zero for parallel vectors, - * and its absolute value highest for perpendicular vectors. + * This is also known as "perp dot product". It will be zero for parallel vectors, + * and the absolute value will be highest for perpendicular vectors. + * @return \f$a \times b = a_X b_Y - a_Y b_X\f$. * @relates Point*/ inline Coord cross(Point const &a, Point const &b) { - return dot(a, b.cw()); + // equivalent implementation: + // return dot(a, b.ccw()); + return a[X] * b[Y] - a[Y] * b[X]; } -/** @brief Compute the (Euclidean) distance between points. - * @relates Point */ -inline Coord distance (Point const &a, Point const &b) -{ - return L2(a - b); +/// Compute the (Euclidean) distance between points. +/// @relates Point +inline Coord distance (Point const &a, Point const &b) { + return (a - b).length(); } -/** @brief Compute the square of the distance between points. - * @relates Point */ -inline Coord distanceSq (Point const &a, Point const &b) -{ +/// Compute the square of the distance between points. +/// @relates Point +inline Coord distanceSq (Point const &a, Point const &b) { return L2sq(a - b); } +//IMPL: NearConcept +/// Test whether two points are no further apart than some threshold. +/// @relates Point +inline bool are_near(Point const &a, Point const &b, double eps = EPSILON) { + // do not use an unqualified calls to distance before the empty + // specialization of iterator_traits is defined - see end of file + return are_near((a - b).length(), 0, eps); +} + +/// Test whether three points lie approximately on the same line. +/// @relates Point +inline bool are_collinear(Point const& p1, Point const& p2, Point const& p3, + double eps = EPSILON) +{ + return are_near( cross(p3, p2) - cross(p3, p1) + cross(p2, p1), 0, eps); +} + Point unit_vector(Point const &a); Coord L1(Point const &p); Coord LInfty(Point const &p); bool is_zero(Point const &p); -bool is_unit_vector(Point const &p); +bool is_unit_vector(Point const &p, Coord eps = EPSILON); double atan2(Point const &p); double angle_between(Point const &a, Point const &b); Point abs(Point const &b); Point constrain_angle(Point const &A, Point const &B, unsigned int n = 4, Geom::Point const &dir = Geom::Point(1,0)); -} /* namespace Geom */ +} // end namespace Geom // This is required to fix a bug in GCC 4.3.3 (and probably others) that causes the compiler // to try to instantiate the iterator_traits template and fail. Probably it thinks that Point @@ -375,7 +418,7 @@ namespace std { template <> class iterator_traits<Geom::Point> {}; } -#endif /* !SEEN_Geom_POINT_H */ +#endif // LIB2GEOM_SEEN_POINT_H /* Local Variables: diff --git a/src/2geom/poly.cpp b/src/2geom/polynomial.cpp index de0229172..ca2389f80 100644 --- a/src/2geom/poly.cpp +++ b/src/2geom/polynomial.cpp @@ -1,4 +1,41 @@ -#include <2geom/poly.h> +/** + * \file + * \brief Polynomial in canonical (monomial) basis + *//* + * Authors: + * MenTaLguY <mental@rydia.net> + * Krzysztof Kosiński <tweenk.pl@gmail.com> + * + * Copyright 2007-2015 Authors + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + */ + +#include <algorithm> +#include <2geom/polynomial.h> +#include <2geom/math-utils.h> +#include <math.h> #ifdef HAVE_GSL #include <gsl/gsl_poly.h> @@ -183,6 +220,100 @@ Poly gcd(Poly const &a, Poly const &b, const double /*tol*/) { + +std::vector<Coord> solve_quadratic(Coord a, Coord b, Coord c) +{ + std::vector<Coord> result; + + if (a == 0) { + // linear equation + if (b == 0) return result; + result.push_back(-c/b); + return result; + } + + Coord delta = b*b - 4*a*c; + + if (delta == 0) { + // one root + result.push_back(-b / (2*a)); + } else if (delta > 0) { + // two roots + Coord delta_sqrt = sqrt(delta); + + // Use different formulas depending on sign of b to preserve + // numerical stability. See e.g.: + // http://people.csail.mit.edu/bkph/articles/Quadratics.pdf + Coord t = -0.5 * (b + sgn(b) * delta_sqrt); + result.push_back(t / a); + result.push_back(c / t); + } + // no roots otherwise + + std::sort(result.begin(), result.end()); + return result; +} + + +std::vector<Coord> solve_cubic(Coord a, Coord b, Coord c, Coord d) +{ + // based on: + // http://mathworld.wolfram.com/CubicFormula.html + + if (a == 0) { + return solve_quadratic(b, c, d); + } + if (d == 0) { + // divide by x + std::vector<Coord> result = solve_quadratic(a, b, c); + result.push_back(0); + std::sort(result.begin(), result.end()); + return result; + } + + std::vector<Coord> result; + + // 1. divide everything by a to bring to canonical form + b /= a; + c /= a; + d /= a; + + // 2. eliminate x^2 term: x^3 + 3Qx - 2R = 0 + Coord Q = (3*c - b*b) / 9; + Coord R = (-27 * d + b * (9*c - 2*b*b)) / 54; + + // 3. compute polynomial discriminant + Coord D = Q*Q*Q + R*R; + Coord term1 = b/3; + + if (D > 0) { + // only one real root + Coord S = cbrt(R + sqrt(D)); + Coord T = cbrt(R - sqrt(D)); + result.push_back(-b/3 + S + T); + } else if (D == 0) { + // 3 real roots, 2 of which are equal + Coord rroot = cbrt(R); + result.reserve(3); + result.push_back(-term1 + 2*rroot); + result.push_back(-term1 - rroot); + result.push_back(-term1 - rroot); + } else { + // 3 distinct real roots + assert(Q < 0); + Coord theta = acos(R / sqrt(-Q*Q*Q)); + Coord rroot = 2 * sqrt(-Q); + result.reserve(3); + result.push_back(-term1 + rroot * cos(theta / 3)); + result.push_back(-term1 + rroot * cos((theta + 2*M_PI) / 3)); + result.push_back(-term1 + rroot * cos((theta + 4*M_PI) / 3)); + } + + std::sort(result.begin(), result.end()); + return result; +} + + /*Poly divide_out_root(Poly const & p, double x) { assert(1); }*/ diff --git a/src/2geom/poly.h b/src/2geom/polynomial.h index 7d93d0a85..5ab2aa4c8 100644 --- a/src/2geom/poly.h +++ b/src/2geom/polynomial.h @@ -3,9 +3,10 @@ * \brief Polynomial in canonical (monomial) basis *//* * Authors: - * ? <?@?.?> + * MenTaLguY <mental@rydia.net> + * Krzysztof Kosiński <tweenk.pl@gmail.com> * - * Copyright ?-? authors + * Copyright 2007-2015 Authors * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public @@ -29,7 +30,6 @@ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. - * */ #ifndef LIB2GEOM_SEEN_POLY_H @@ -39,10 +39,13 @@ #include <iostream> #include <algorithm> #include <complex> +#include <2geom/coord.h> #include <2geom/utils.h> namespace Geom { +/** @brief Polynomial in canonical (monomial) basis. + * @ingroup Fragments */ class Poly : public std::vector<double>{ public: // coeff; // sum x^i*coeff[i] @@ -214,6 +217,18 @@ std::vector<double> solve_reals(const Poly & p); #endif double polish_root(Poly const & p, double guess, double tol); + +/** @brief Analytically solve quadratic equation. + * The equation is given in the standard form: ax^2 + bx + c = 0. + * Only real roots are returned. */ +std::vector<Coord> solve_quadratic(Coord a, Coord b, Coord c); + +/** @brief Analytically solve cubic equation. + * The equation is given in the standard form: ax^3 + bx^2 + cx + d = 0. + * Only real roots are returned. */ +std::vector<Coord> solve_cubic(Coord a, Coord b, Coord c, Coord d); + + inline std::ostream &operator<< (std::ostream &out_file, const Poly &in_poly) { if(in_poly.size() == 0) out_file << "0"; diff --git a/src/2geom/quadtree.cpp b/src/2geom/quadtree.cpp deleted file mode 100644 index 98030c424..000000000 --- a/src/2geom/quadtree.cpp +++ /dev/null @@ -1,290 +0,0 @@ -#include <2geom/quadtree.h> - -namespace Geom{ -Quad* QuadTree::search(Rect const &r) { - return search(r[0].min(), r[1].min(), - r[0].max(), r[1].max()); -} - -void QuadTree::insert(Rect const &r, int shape) { - insert(r[0].min(), r[1].min(), - r[0].max(), r[1].max(), shape); -} - - -Quad* QuadTree::search(double x0, double y0, double x1, double y1) { - Quad *q = root; - - double bxx0 = bx1, bxx1 = bx1; - double byy0 = by1, byy1 = by1; - while(q) { - double cx = (bxx0 + bxx1)/2; - double cy = (byy0 + byy1)/2; - unsigned i = 0; - if(x0 >= cx) { - i += 1; - bxx0 = cx; // zoom in a quad - } else if(x1 <= cx) { - bxx1 = cx; - } else - break; - if(y0 >= cy) { - i += 2; - byy0 = cy; - } else if(y1 <= cy) { - byy1 = cy; - } else - break; - - assert(i < 4); - Quad *qq = q->children[i]; - if(qq == 0) break; // last non-null - q = qq; - } - return q; -} - - -/* -Comments by Vangelis (use with caution :P ) - -Insert Rect (x0, y0), (x1, y1) in the QuadTree Q. - -=================================================================================== -* QuadTree Q has: Quadtree's Quad root R, QuadTree's bounding box B. - -* Each Quad has a Quad::data where we store the id of the Rect that belong to -this Quad. (In reality we'll store a pointer to the shape). - -* Each Quad has 4 Quad children: 0, 1, 2, 3. Each child Quad represents one of the following quarters -of the bounding box B: - -+---------------------+ -| | | -| NW=0 | NE=1 | -| | | -| | | -+---------------------+ -| | | -| SW=2 | SE=3 | -| | | -| | | -+---------------------+ - -Each Quad can further be divided in 4 Quads as above and so on. Below there is an example - - Root - / || \ - / / \ \ - 0 1 2 3 - /\ - / | | \ - 0 1 2 3 - -+---------------------+ -| | 1-0 | 1-1| -| 0 | | | -| |-----|----| -| | 1-2 | 1-3| -| | | | -+---------------------+ -| | | -| | | -| 2 | 3 | -| | | -+---------------------+ - - - -=================================================================================== -Insert Rect (x0, y0), (x1, y1) in the QuadTree Q. Algorithm: -1) check if Rect is bigger than QuadTree's bounding box -2) find in which Quad we should add the Rect: - - - ------------------------------------------------------------------------------------ -How we find in which Quad we should add the Rect R: - -Q = Quadtree's Quad root -B = QuadTree's bounding box B -WHILE (Q) { - IF ( Rect cannot fit in one unique quarter of B ){ - Q = current Quad ; - BREAK; - } - IF ( Rect can fit in the quarter I ) { - IF (Q.children[I] doesn't exist) { - create the Quad Q.children[I]; - } - B = bounding box of the Quad Q.children[I] ; - Q = Q.children[I] ; - CHECK(R, B) ; - } -} -add Rect R to Q ; - - -*/ - -void QuadTree::insert(double x0, double y0, double x1, double y1, int shape) { - // loop until a quad would break the box. - - // empty root => empty QuadTree. Create initial bounding box (0,0), (1,1) - if(root == NULL) { - root = new Quad; - - bx0 = 0; - bx1 = 1; - by0 = 0; - by1 = 1; - } - Quad *q = root; - - //A temp bounding box. Same as root's bounting box (ie of the whole QuadTree) - double bxx0 = bx0, bxx1 = bx1; - double byy0 = by0, byy1 = by1; - - while((bxx0 > x0) || - (bxx1 < x1) || - (byy0 > y0) || - (byy1 < y1)) { - // QuadTree has small size, can't accomodate new rect. Double the size: - unsigned i = 0; - - if(bxx0 > x0) { - bxx0 = 2*bxx0 - bxx1; - i += 1; - } else { - bxx1 = 2*bxx1 - bxx0; - } - if(byy0 > y0) { - byy0 = 2*byy0 - byy1; - i += 2; - } else { - byy1 = 2*byy1 - byy0; - } - q = new Quad; - //check if root is empty (no rects, no quad children) - if( clean_root() ){ - root = q; - } - else{ - q->children[i] = root; - root = q; - } - bx0 = bxx0; - bx1 = bxx1; - by0 = byy0; - by1 = byy1; - } - - while(true) { - // Find the center of the temp bounding box - double cx = (bxx0 + bxx1)/2; - double cy = (byy0 + byy1)/2; - unsigned i = 0; - assert(x0 >= bxx0); - assert(x1 <= bxx1); - assert(y0 >= byy0); - assert(y1 <= byy1); - - if(x0 >= cx) { - i += 1; - bxx0 = cx; // zoom in a quad - } else if(x1 <= cx) { - bxx1 = cx; - } else{ - // rect does not fit in one unique quarter (in X axis) of the temp bounding box - break; - } - if(y0 >= cy) { - i += 2; - byy0 = cy; - } else if(y1 <= cy) { - byy1 = cy; - } else{ - // rect does not fit in one unique quarter (in Y axis) of the temp bounding box - break; - } - - // check if rect's bounding box has size 1x1. This means that rect is defined by 2 points - // that are in the same place. - if( ( fabs(bxx0 - bxx1) < 1.0 ) && ( fabs(byy0 - byy1) < 1.0 )){ - bxx0 = floor(bxx0); - bxx1 = floor(bxx1); - byy0 = floor(byy0); - byy1 = floor(byy1); - break; - } - - /* - 1 rect does fit in one unique quarter of the temp bounding box. And we have found which. - 2 temp bounding box = bounding box of this quarter. - 3 "Go in" this quarter (create if doesn't exist) - */ - assert(i < 4); - Quad *qq = q->children[i]; - if(qq == NULL) { - qq = new Quad; - q->children[i] = qq; - } - q = qq; - } - q->data.push_back(shape); -} - - -void QuadTree::erase(Quad *q, int shape) { - for(Quad::iterator i = q->data.begin(); i != q->data.end(); ++i) { - if(*i == shape) { - q->data.erase(i); - if(q->data.empty()) { - - } - } - } - return; -} - -/* -Returns: -false: if root isn't empty -true: if root is empty it cleans root -*/ -bool QuadTree::clean_root() { - assert(root); - - // false if root *has* rects assigned to it. - bool all_clean = root->data.empty(); - - // if root has children we get false - for(unsigned i = 0; i < 4; i++) - { - if(root->children[i]) - { - all_clean = false; - } - } - - if(all_clean) - { - delete root; - root=0; - return true; - } - 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/2geom/quadtree.h b/src/2geom/quadtree.h deleted file mode 100644 index 949a9b898..000000000 --- a/src/2geom/quadtree.h +++ /dev/null @@ -1,99 +0,0 @@ -/** - * \file - * \brief Quad tree data structure - *//* - * Authors: - * ? <?@?.?> - * - * Copyright ?-? authors - * - * This library is free software; you can redistribute it and/or - * modify it either under the terms of the GNU Lesser General Public - * License version 2.1 as published by the Free Software Foundation - * (the "LGPL") or, at your option, under the terms of the Mozilla - * Public License Version 1.1 (the "MPL"). If you do not alter this - * notice, a recipient may use your version of this file under either - * the MPL or the LGPL. - * - * You should have received a copy of the LGPL along with this library - * in the file COPYING-LGPL-2.1; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * You should have received a copy of the MPL along with this library - * in the file COPYING-MPL-1.1 - * - * The contents of this file are subject to the Mozilla Public License - * Version 1.1 (the "License"); you may not use this file except in - * compliance with the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY - * OF ANY KIND, either express or implied. See the LGPL or the MPL for - * the specific language governing rights and limitations. - * - */ - -#include <vector> -#include <cassert> - -#include <2geom/d2.h> - -namespace Geom{ - -class Quad{ -public: - Quad* children[4]; - std::vector<int> data; - Quad() { - for(int i = 0; i < 4; i++) - children[i] = 0; - } - typedef std::vector<int>::iterator iterator; - Rect bounds(unsigned i, double x, double y, double d) { - double dd = d/2; - switch(i % 4) { - case 0: - return Rect(Interval(x, x+dd), Interval(y, y+dd)); - case 1: - return Rect(Interval(x+dd, x+d), Interval(y, y+dd)); - case 2: - return Rect(Interval(x, x+dd), Interval(y+dd, y+d)); - case 3: - return Rect(Interval(x+dd, x+d), Interval(y+dd, y+d)); - default: - /* just to suppress warning message - * this case should be never reached */ - assert(false); - } - } -}; - -class QuadTree{ -public: - Quad* root; - double scale; - double bx0, bx1; - double by0, by1; - - QuadTree() : root(0), scale(1) {} - - Quad* search(double x0, double y0, double x1, double y1); - void insert(double x0, double y0, double x1, double y1, int shape); - Quad* search(Geom::Rect const &r); - void insert(Geom::Rect const &r, int shape); - void erase(Quad *q, int shape); -private: - bool clean_root(); -}; - -}; - -/* - 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/2geom/ray.h b/src/2geom/ray.h index 2fda06ff5..a336e3132 100644 --- a/src/2geom/ray.h +++ b/src/2geom/ray.h @@ -98,7 +98,7 @@ public: } return result; } - Coord nearestPoint(Point const& point) const { + Coord nearestTime(Point const& point) const { if ( isDegenerate() ) return 0; double t = dot(point - _origin, _versor); if (t < 0) t = 0; @@ -123,7 +123,7 @@ public: inline double distance(Point const& _point, Ray const& _ray) { - double t = _ray.nearestPoint(_point); + double t = _ray.nearestTime(_point); return ::Geom::distance(_point, _ray.pointAt(t)); } diff --git a/src/2geom/rect.cpp b/src/2geom/rect.cpp index 0cb842d29..383e72c8e 100644 --- a/src/2geom/rect.cpp +++ b/src/2geom/rect.cpp @@ -84,6 +84,17 @@ Coord distance(Point const &p, Rect const &rect) return hypot(dx, dy); } +Coord distanceSq(Point const &p, OptRect const &rect) +{ + if (!rect) return std::numeric_limits<Coord>::max(); + return distanceSq(p, *rect); +} +Coord distance(Point const &p, OptRect const &rect) +{ + if (!rect) return std::numeric_limits<Coord>::max(); + return distance(p, *rect); +} + } // namespace Geom /* diff --git a/src/2geom/rect.h b/src/2geom/rect.h index 2516bcfa6..51daf6b5a 100644 --- a/src/2geom/rect.h +++ b/src/2geom/rect.h @@ -75,6 +75,10 @@ public: * @param eps Maximum value of the area to consider empty * @return True if rectangle has an area smaller than tolerance, false otherwise */ bool hasZeroArea(Coord eps = EPSILON) const { return (area() <= eps); } + /// Check whether the rectangle has finite area + bool isFinite() const { return (*this)[X].isFinite() && (*this)[Y].isFinite(); } + /// Calculate the diameter of the smallest circle that would contain the rectangle. + Coord diameter() const { return distance(corner(0), corner(2)); } /// @} /// @name Test other rectangles and points for inclusion. @@ -153,6 +157,10 @@ public: Coord distanceSq(Point const &p, Rect const &rect); Coord distance(Point const &p, Rect const &rect); +/// Minimum square of distance to rectangle, or infinity if empty. +Coord distanceSq(Point const &p, OptRect const &rect); +/// Minimum distance to rectangle, or infinity if empty. +Coord distance(Point const &p, OptRect const &rect); inline bool Rect::interiorContains(OptRect const &r) const { return !r || interiorContains(static_cast<Rect const &>(*r)); diff --git a/src/2geom/recursive-bezier-intersection.cpp b/src/2geom/recursive-bezier-intersection.cpp index 548065196..e86192f54 100644 --- a/src/2geom/recursive-bezier-intersection.cpp +++ b/src/2geom/recursive-bezier-intersection.cpp @@ -13,7 +13,6 @@ unsigned intersect_steps = 0; using std::vector; -using std::swap; namespace Geom { @@ -33,7 +32,7 @@ public: minax = p[0][X]; // These are the most likely to be extremal maxax = p.back()[X]; if( minax > maxax ) - swap(minax, maxax); + std::swap(minax, maxax); for(unsigned i = 1; i < p.size()-1; i++) { if( p[i][X] < minax ) minax = p[i][X]; @@ -44,7 +43,7 @@ public: minay = p[0][Y]; // These are the most likely to be extremal maxay = p.back()[Y]; if( minay > maxay ) - swap(minay, maxay); + std::swap(minay, maxay); for(unsigned i = 1; i < p.size()-1; i++) { if( p[i][Y] < minay ) minay = p[i][Y]; diff --git a/src/2geom/region.cpp b/src/2geom/region.cpp deleted file mode 100644 index 8cfb1c68c..000000000 --- a/src/2geom/region.cpp +++ /dev/null @@ -1,45 +0,0 @@ -#include <2geom/region.h> -#include <2geom/utils.h> - -#include <2geom/shape.h> - -namespace Geom { - -Region Region::operator*(Affine const &m) const { - Region r((m.flips() ? boundary.reverse() : boundary) * m, fill); - if(box && m.isZoom()) r.box = (*box) * m; - return r; -} - -bool Region::invariants() const { - return self_crossings(boundary).empty(); -} - -unsigned outer_index(Regions const &ps) { - if(ps.size() <= 1 || ps[0].contains(ps[1])) { - return 0; - } else { - /* Since we've already shown that chunks[0] is not outside - it can be used as an exemplar inner. */ - Point exemplar = Path(ps[0]).initialPoint(); - for(unsigned i = 1; i < ps.size(); i++) { - if(ps[i].contains(exemplar)) { - return i; - } - } - } - return ps.size(); -} - -} - -/* - 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/2geom/region.h b/src/2geom/region.h deleted file mode 100644 index 06a4f63e9..000000000 --- a/src/2geom/region.h +++ /dev/null @@ -1,130 +0,0 @@ -/** - * \file - * \brief Uncrossed path for boolean algorithms - *//* - * Authors: - * ? <?@?.?> - * - * Copyright ?-? authors - * - * This library is free software; you can redistribute it and/or - * modify it either under the terms of the GNU Lesser General Public - * License version 2.1 as published by the Free Software Foundation - * (the "LGPL") or, at your option, under the terms of the Mozilla - * Public License Version 1.1 (the "MPL"). If you do not alter this - * notice, a recipient may use your version of this file under either - * the MPL or the LGPL. - * - * You should have received a copy of the LGPL along with this library - * in the file COPYING-LGPL-2.1; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * You should have received a copy of the MPL along with this library - * in the file COPYING-MPL-1.1 - * - * The contents of this file are subject to the Mozilla Public License - * Version 1.1 (the "License"); you may not use this file except in - * compliance with the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY - * OF ANY KIND, either express or implied. See the LGPL or the MPL for - * the specific language governing rights and limitations. - * - */ - -#ifndef __2GEOM_REGION_H -#define __2GEOM_REGION_H - -#include <2geom/path.h> -#include <2geom/path-intersection.h> - -namespace Geom { - -class Shape; - -class Region { - friend Crossings crossings(Region const &a, Region const &b); - friend class Shape; - friend Shape shape_boolean(bool rev, Shape const & a, Shape const & b, CrossingSet const & crs); - - Path boundary; - mutable OptRect box; - bool fill; - public: - Region() : fill(true) {} - explicit Region(Path const &p) : boundary(p) { fill = path_direction(p); } - Region(Path const &p, bool dir) : boundary(p), fill(dir) {} - Region(Path const &p, OptRect const &b) : boundary(p), box(b) { fill = path_direction(p); } - Region(Path const &p, OptRect const &b, bool dir) : boundary(p), box(b), fill(dir) {} - - unsigned size() const { return boundary.size(); } - - bool isFill() const { return fill; } - Region asFill() const { if(fill) return Region(*this); else return inverse(); } - Region asHole() const { if(fill) return inverse(); else return Region(*this); } - - operator Path() const { return boundary; } - Rect boundsFast() const { - if(!box) box = boundary.boundsFast(); /// \todo this doesn't look right at all... - return *box; - } - - bool contains(Point const &p) const { - if(box && !box->contains(p)) return false; - return Geom::contains(boundary, p); - } - bool contains(Region const &other) const { return contains(other.boundary.initialPoint()); } - - bool includes(Point const &p) const { - return logical_xor(!fill, contains(p)); - } - - Region inverse() const { return Region(boundary.reverse(), box, !fill); } - - Region operator*(Affine const &m) const; - - bool invariants() const; -}; - -typedef std::vector<Region> Regions; - -unsigned outer_index(Regions const &ps); - -//assumes they're already sanitized somewhat -inline Regions regions_from_paths(std::vector<Path> const &ps) { - Regions res; - for(unsigned i = 0; i < ps.size(); i++) - res.push_back(Region(ps[i])); - return res; -} - -inline std::vector<Path> paths_from_regions(Regions const &rs) { - std::vector<Path> res; - for(unsigned i = 0; i < rs.size(); i++) - res.push_back(rs[i]); - return res; -} - -Regions sanitize_path(Path const &p); - -Regions region_boolean(bool rev, Region const & a, Region const & b, Crossings const &cr); -Regions region_boolean(bool rev, Region const & a, Region const & b, Crossings const & cr_a, Crossings const & cr_b); - -inline Regions region_boolean(bool rev, Region const & a, Region const & b) { - return region_boolean(rev, a, b, crossings(a, b)); -} - -} - -#endif - -/* - 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/2geom/sbasis-2d.cpp b/src/2geom/sbasis-2d.cpp index aa5018e9e..53b09cd35 100644 --- a/src/2geom/sbasis-2d.cpp +++ b/src/2geom/sbasis-2d.cpp @@ -169,7 +169,7 @@ sb2d_cubic_solve(SBasis2d const &f, Geom::Point const &A, Geom::Point const &B){ std::vector<D2<SBasis> > candidates = cubics_fitting_curvature(A,B,V0,V1,D2fVV0,D2fVV1); if (candidates.empty()) { - return D2<SBasis>(Linear(A[X],B[X]),Linear(A[Y],B[Y])); + return D2<SBasis>(SBasis(Linear(A[X],B[X])), SBasis(Linear(A[Y],B[Y]))); } //TODO: I'm sure std algorithm could do that for me... double error = -1; diff --git a/src/2geom/sbasis-2d.h b/src/2geom/sbasis-2d.h index 00429e259..c7d9b000a 100644 --- a/src/2geom/sbasis-2d.h +++ b/src/2geom/sbasis-2d.h @@ -33,8 +33,8 @@ * */ -#ifndef SEEN_SBASIS_2D_H -#define SEEN_SBASIS_2D_H +#ifndef LIB2GEOM_SEEN_SBASIS_2D_H +#define LIB2GEOM_SEEN_SBASIS_2D_H #include <vector> #include <cassert> #include <algorithm> @@ -356,8 +356,9 @@ sb2dsolve(SBasis2d const &f, Geom::Point const &A, Geom::Point const &B, unsigne D2<SBasis> sb2d_cubic_solve(SBasis2d const &f, Geom::Point const &A, Geom::Point const &B); -}; +} // end namespace Geom +#endif /* Local Variables: mode:c++ @@ -368,4 +369,3 @@ sb2d_cubic_solve(SBasis2d const &f, Geom::Point const &A, Geom::Point const &B); End: */ // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : -#endif diff --git a/src/2geom/sbasis-curve.h b/src/2geom/sbasis-curve.h index a5c3f2ca7..affe7edc0 100644 --- a/src/2geom/sbasis-curve.h +++ b/src/2geom/sbasis-curve.h @@ -37,7 +37,7 @@ #define LIB2GEOM_SEEN_SBASIS_CURVE_H #include <2geom/curve.h> -#include <2geom/nearest-point.h> +#include <2geom/nearest-time.h> #include <2geom/sbasis-geometric.h> #include <2geom/transforms.h> @@ -84,11 +84,11 @@ public: explicit SBasisCurve(D2<SBasis> const &sb) : inner(sb) {} explicit SBasisCurve(Curve const &other) : inner(other.toSBasis()) {} -#ifndef DOXYGEN_SHOULD_SKIP_THIS virtual Curve *duplicate() const { return new SBasisCurve(*this); } virtual Point initialPoint() const { return inner.at0(); } virtual Point finalPoint() const { return inner.at1(); } - virtual bool isDegenerate() const { return inner.isConstant(); } + virtual bool isDegenerate() const { return inner.isConstant(0); } + virtual bool isLineSegment() const { return inner[X].size() == 1; } virtual Point pointAt(Coord t) const { return inner.valueAt(t); } virtual std::vector<Point> pointAndDerivatives(Coord t, unsigned n) const { return inner.valueAndDerivatives(t, n); @@ -106,33 +106,34 @@ public: return bounds_local(inner, i, deg); } virtual std::vector<Coord> roots(Coord v, Dim2 d) const { return Geom::roots(inner[d] - v); } - virtual Coord nearestPoint( Point const& p, Coord from = 0, Coord to = 1 ) const { - return nearest_point(p, inner, from, to); + virtual Coord nearestTime( Point const& p, Coord from = 0, Coord to = 1 ) const { + return nearest_time(p, inner, from, to); } - virtual std::vector<Coord> allNearestPoints( Point const& p, Coord from = 0, + virtual std::vector<Coord> allNearestTimes( Point const& p, Coord from = 0, Coord to = 1 ) const { - return all_nearest_points(p, inner, from, to); + return all_nearest_times(p, inner, from, to); } virtual Coord length(Coord tolerance) const { return ::Geom::length(inner, tolerance); } virtual Curve *portion(Coord f, Coord t) const { return new SBasisCurve(Geom::portion(inner, f, t)); } - virtual Curve *transformed(Affine const &m) const { return new SBasisCurve(inner * m); } - virtual Curve &operator*=(Translate const &m) { - inner += m.vector(); - return *this; - }; + using Curve::operator*=; + virtual void operator*=(Affine const &m) { inner = inner * m; } virtual Curve *derivative() const { return new SBasisCurve(Geom::derivative(inner)); } virtual D2<SBasis> toSBasis() const { return inner; } + virtual bool operator==(Curve const &c) const { + SBasisCurve const *other = dynamic_cast<SBasisCurve const *>(&c); + if (!other) return false; + return inner == other->inner; + } virtual int degreesOfFreedom() const { return inner[0].degreesOfFreedom() + inner[1].degreesOfFreedom(); } -#endif }; } // end namespace Geom diff --git a/src/2geom/sbasis-geometric.h b/src/2geom/sbasis-geometric.h index 43c624fb4..7f1e8aaba 100644 --- a/src/2geom/sbasis-geometric.h +++ b/src/2geom/sbasis-geometric.h @@ -1,15 +1,6 @@ -#ifndef _SBASIS_GEOMETRIC -#define _SBASIS_GEOMETRIC -#include <2geom/d2.h> -#include <2geom/piecewise.h> -#include <vector> - /** * \file * \brief two-dimensional geometric operators. - * - * Copyright 2007, JFBarraud - * Copyright 2007, njh * * These operators are built on a more 'polynomially robust' * transformation to map a function that takes a [0,1] parameter to a @@ -21,9 +12,42 @@ * from the various tangent directions at each end (angular jet). As * a result, the final path has a convergence behaviour derived from * that of the sin and cos series. -- njh + *//* + * Copyright 2007, JFBarraud + * Copyright 2007, njh + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. */ -namespace Geom{ +#ifndef LIB2GEOM_SEEN_SBASIS_GEOMETRIC_H +#define LIB2GEOM_SEEN_SBASIS_GEOMETRIC_H + +#include <2geom/d2.h> +#include <2geom/piecewise.h> +#include <vector> + +namespace Geom { Piecewise<D2<SBasis> > cutAtRoots(Piecewise<D2<SBasis> > const &M, double tol=1e-4); diff --git a/src/2geom/sbasis-math.cpp b/src/2geom/sbasis-math.cpp index 97cdf45ce..896eb18a7 100644 --- a/src/2geom/sbasis-math.cpp +++ b/src/2geom/sbasis-math.cpp @@ -297,11 +297,11 @@ Piecewise<SBasis> reciprocalOnDomain(Interval range, double tol){ if (a<=tol){ reciprocal_fn.push_cut(0); int i0=(int) floor(std::log(tol)/std::log(R)); - a=pow(R,i0); + a = std::pow(R,i0); reciprocal_fn.push(Linear(1/a),a); }else{ int i0=(int) floor(std::log(a)/std::log(R)); - a=pow(R,i0); + a = std::pow(R,i0); reciprocal_fn.cuts.push_back(a); } diff --git a/src/2geom/sbasis-math.h b/src/2geom/sbasis-math.h index e6d40a3de..e191dae62 100644 --- a/src/2geom/sbasis-math.h +++ b/src/2geom/sbasis-math.h @@ -1,7 +1,6 @@ -/** - * \file - * \brief some std functions to work with (pw)s-basis - * +/** @file + * @brief some std functions to work with (pw)s-basis + *//* * Authors: * Jean-Francois Barraud * @@ -36,8 +35,8 @@ //TODO: in all these functions, compute 'order' according to 'tol'. //TODO: use template to define the pw version automatically from the sb version? -#ifndef SEEN_GEOM_SB_CALCULS_H -#define SEEN_GEOM_SB_CALCULS_H +#ifndef LIB2GEOM_SEEN_SBASIS_MATH_H +#define LIB2GEOM_SEEN_SBASIS_MATH_H #include <2geom/sbasis.h> diff --git a/src/2geom/sbasis-poly.h b/src/2geom/sbasis-poly.h index e0bef9333..d18bc369e 100644 --- a/src/2geom/sbasis-poly.h +++ b/src/2geom/sbasis-poly.h @@ -1,13 +1,6 @@ -#ifndef _SBASIS_TO_POLY -#define _SBASIS_TO_POLY - -#include <2geom/poly.h> -#include <2geom/sbasis.h> - -/** - * \file - * \brief Conversion between SBasis and Poly. Not recommended for general use due to instability. - * +/** @file + * @brief Conversion between SBasis and Poly. Not recommended for general use due to instability. + *//* * Authors: * ? <?@?.?> * @@ -35,9 +28,14 @@ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. - * */ +#ifndef LIB2GEOM_SEEN_SBASIS_POLY_H +#define LIB2GEOM_SEEN_SBASIS_POLY_H + +#include <2geom/polynomial.h> +#include <2geom/sbasis.h> + namespace Geom{ SBasis poly_to_sbasis(Poly const & p); @@ -45,6 +43,7 @@ Poly sbasis_to_poly(SBasis const & s); }; +#endif /* Local Variables: mode:c++ @@ -55,5 +54,3 @@ Poly sbasis_to_poly(SBasis const & s); End: */ // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : - -#endif diff --git a/src/2geom/sbasis-roots.cpp b/src/2geom/sbasis-roots.cpp index acf4e1abc..57bef4c0f 100644 --- a/src/2geom/sbasis-roots.cpp +++ b/src/2geom/sbasis-roots.cpp @@ -1,7 +1,38 @@ -/** root finding for sbasis functions. - * Copyright 2006 N Hurst - * Copyright 2007 JF Barraud +/** + * @file + * @brief Root finding for sbasis functions. + *//* + * Authors: + * Nathan Hurst <njh@njhurst.com> + * JF Barraud + * Copyright 2006-2007 Authors + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + */ + + /* * It is more efficient to find roots of f(t) = c_0, c_1, ... all at once, rather than iterating. * * Todo/think about: @@ -98,20 +129,20 @@ OptInterval bounds_fast(const SBasis &sb, int order) { double b=sb[j][1]; double v, t = 0; - v = res[0]; + v = res.min(); if (v<0) t = ((b-a)/v+1)*0.5; if (v>=0 || t<0 || t>1) { - res[0] = std::min(a,b); - }else{ - res[0]=lerp(t, a+v*t, b); + res.setMin(std::min(a,b)); + } else { + res.setMin(lerp(t, a+v*t, b)); } - v = res[1]; + v = res.max(); if (v>0) t = ((b-a)/v+1)*0.5; if (v<=0 || t<0 || t>1) { - res[1] = std::max(a,b); + res.setMax(std::max(a,b)); }else{ - res[1]=lerp(t, a+v*t, b); + res.setMax(lerp(t, a+v*t, b)); } } if (order>0) res*=std::pow(.25,order); @@ -576,6 +607,7 @@ std::vector<double> roots1(SBasis const & s, Interval const ivl) { /** Find all t s.t s(t) = 0 \param a sbasis function + \see Bezier::roots \returns vector of zeros (roots) */ diff --git a/src/2geom/sbasis-to-bezier.cpp b/src/2geom/sbasis-to-bezier.cpp index a2e4253d2..09fbb03ef 100644 --- a/src/2geom/sbasis-to-bezier.cpp +++ b/src/2geom/sbasis-to-bezier.cpp @@ -37,7 +37,7 @@ #include <2geom/choose.h> #include <2geom/path-sink.h> #include <2geom/exception.h> -#include <2geom/convex-cover.h> +#include <2geom/convex-hull.h> #include <iostream> @@ -153,6 +153,15 @@ void sbasis_to_bezier (Bezier & bz, SBasis const& sb, size_t sz) bz[n] = sb[0][1]; } +void sbasis_to_bezier(D2<Bezier> &bz, D2<SBasis> const &sb, size_t sz) +{ + if (sz == 0) { + sz = std::max(sb[X].size(), sb[Y].size())*2; + } + sbasis_to_bezier(bz[X], sb[X], sz); + sbasis_to_bezier(bz[Y], sb[Y], sz); +} + /** Changes the basis of p to be Bernstein. \param p the D2 Symmetric basis polynomial \returns the D2 Bernstein basis polynomial @@ -161,24 +170,9 @@ void sbasis_to_bezier (Bezier & bz, SBasis const& sb, size_t sz) */ void sbasis_to_bezier (std::vector<Point> & bz, D2<SBasis> const& sb, size_t sz) { - Bezier bzx, bzy; - if(sz == 0) { - sz = std::max(sb[X].size(), sb[Y].size())*2; - } - sbasis_to_bezier(bzx, sb[X], sz); - sbasis_to_bezier(bzy, sb[Y], sz); - assert(bzx.size() == bzy.size()); - size_t n = (bzx.size() >= bzy.size()) ? bzx.size() : bzy.size(); - - bz.resize(n, Point(0,0)); - for (size_t i = 0; i < bzx.size(); ++i) - { - bz[i][X] = bzx[i]; - } - for (size_t i = 0; i < bzy.size(); ++i) - { - bz[i][Y] = bzy[i]; - } + D2<Bezier> bez; + sbasis_to_bezier(bez, sb, sz); + bz = bezier_points(bez); } /** Changes the basis of p to be Bernstein. @@ -238,7 +232,7 @@ void sbasis_to_cubic_bezier (std::vector<Point> & bz, D2<SBasis> const& sb) // is midpoint in hull: if not, the solution will be ill-conditioned, LP Bug 1428683 - if (!bezhull.contains_point(Geom::Point(midx, midy))) + if (!bezhull.contains(Geom::Point(midx, midy))) return; // calculate Bezier control arms @@ -264,11 +258,15 @@ void sbasis_to_cubic_bezier (std::vector<Point> & bz, D2<SBasis> const& sb) dely[1] = 0; } else if (std::abs(xprime[1]*yprime[0] - yprime[1]*xprime[0]) > // general case : fit mid fxn value 0.002 * std::abs(xprime[1]*xprime[0] + yprime[1]*yprime[0])) { // approx. 0.1 degree of angle - denom = xprime[1]*yprime[0] - yprime[1]*xprime[0]; + double test1 = (bz[1][Y] - bz[0][Y])*(bz[3][X] - bz[0][X]) - (bz[1][X] - bz[0][X])*(bz[3][Y] - bz[0][Y]); + double test2 = (bz[2][Y] - bz[0][Y])*(bz[3][X] - bz[0][X]) - (bz[2][X] - bz[0][X])*(bz[3][Y] - bz[0][Y]); + if (test1*test2 < 0) // reject anti-symmetric case, LP Bug 1428267 & Bug 1428683 + return; + denom = 3.0*(xprime[1]*yprime[0] - yprime[1]*xprime[0]); for (int i = 0; i < 2; ++i) { numer = xprime[1 - i]*midy - yprime[1 - i]*midx; - delx[i] = xprime[i]*numer/denom/3; - dely[i] = yprime[i]*numer/denom/3; + delx[i] = xprime[i]*numer/denom; + dely[i] = yprime[i]*numer/denom; } } else if ((xprime[0]*xprime[1] < 0) || (yprime[0]*yprime[1] < 0)) { // symmetric case : use distance of closest approach numer = midx*xprime[0] + midy*yprime[0]; @@ -514,7 +512,7 @@ path_from_sbasis(D2<SBasis> const &B, double tol, bool only_cubicbeziers) { If only_cubicbeziers is true, the resulting path may only contain CubicBezier curves. TODO: some of this logic should be lifted into svg-path */ -std::vector<Geom::Path> +PathVector path_from_piecewise(Geom::Piecewise<Geom::D2<Geom::SBasis> > const &B, double tol, bool only_cubicbeziers) { Geom::PathBuilder pb; if(B.size() == 0) return pb.peek(); diff --git a/src/2geom/sbasis-to-bezier.h b/src/2geom/sbasis-to-bezier.h index 07511f4a4..eadb47bff 100644 --- a/src/2geom/sbasis-to-bezier.h +++ b/src/2geom/sbasis-to-bezier.h @@ -32,11 +32,11 @@ * */ -#ifndef _SBASIS_TO_BEZIER -#define _SBASIS_TO_BEZIER +#ifndef LIB2GEOM_SEEN_SBASIS_TO_BEZIER_H +#define LIB2GEOM_SEEN_SBASIS_TO_BEZIER_H #include <2geom/d2.h> -#include <2geom/path.h> +#include <2geom/pathvector.h> #include <vector> @@ -44,7 +44,8 @@ namespace Geom { class PathBuilder; -void sbasis_to_bezier (Bezier & bz, SBasis const& sb, size_t sz = 0); +void sbasis_to_bezier (Bezier &bz, SBasis const &sb, size_t sz = 0); +void sbasis_to_bezier (D2<Bezier> &bz, D2<SBasis> const &sb, size_t sz = 0); void sbasis_to_bezier (std::vector<Point> & bz, D2<SBasis> const& sb, size_t sz = 0); void sbasis_to_cubic_bezier (std::vector<Point> & bz, D2<SBasis> const& sb); void bezier_to_sbasis (SBasis & sb, Bezier const& bz); @@ -65,7 +66,7 @@ sbasis_to_bezier(D2<SBasis> const &B, unsigned q = 0); #endif -std::vector<Path> path_from_piecewise(Piecewise<D2<SBasis> > const &B, double tol, bool only_cubicbeziers = false); +PathVector path_from_piecewise(Piecewise<D2<SBasis> > const &B, double tol, bool only_cubicbeziers = false); Path path_from_sbasis(D2<SBasis> const &B, double tol, bool only_cubicbeziers = false); inline Path cubicbezierpath_from_sbasis(D2<SBasis> const &B, double tol) @@ -73,10 +74,7 @@ inline Path cubicbezierpath_from_sbasis(D2<SBasis> const &B, double tol) } // end namespace Geom - - #endif - /* Local Variables: mode:c++ diff --git a/src/2geom/sbasis.cpp b/src/2geom/sbasis.cpp index b56e03c74..42d92d7b8 100644 --- a/src/2geom/sbasis.cpp +++ b/src/2geom/sbasis.cpp @@ -328,9 +328,9 @@ SBasis derivative(SBasis const &a) { } int k = a.size()-1; double d = (2*k+1)*(a[k][1] - a[k][0]); - if(d == 0) + if (d == 0 && k > 0) { c.pop_back(); - else { + } else { c[k][0] = d; c[k][1] = d; } @@ -351,9 +351,9 @@ void SBasis::derive() { // in place version } int k = size()-1; double d = (2*k+1)*((*this)[k][1] - (*this)[k][0]); - if(d == 0) + if (d == 0 && k > 0) { pop_back(); - else { + } else { (*this)[k][0] = d; (*this)[k][1] = d; } @@ -466,6 +466,15 @@ SBasis compose(SBasis const &a, SBasis const &b, unsigned k) { return r; } +SBasis portion(const SBasis &t, double from, double to) { + double fv = t.valueAt(from); + double tv = t.valueAt(to); + SBasis ret = compose(t, Linear(from, to)); + ret.at0() = fv; + ret.at1() = tv; + return ret; +} + /* Inversion algorithm. The notation is certainly very misleading. The pseudocode should say: @@ -632,7 +641,6 @@ SBasis compose_inverse(SBasis const &f, SBasis const &g, unsigned order, double //TODO: handle det~0!! if (fabs(det)<zero){ - det = zero; a=b=0; }else{ a=( q01*r10-q10*r01)/det; diff --git a/src/2geom/sbasis.h b/src/2geom/sbasis.h index ca864ac7c..787e8b722 100644 --- a/src/2geom/sbasis.h +++ b/src/2geom/sbasis.h @@ -1,7 +1,6 @@ -/** - * \file - * \brief Defines S-power basis function class - * +/** @file + * @brief Polynomial in symmetric power basis (S-basis) + *//* * Authors: * Nathan Hurst <njh@mail.csse.monash.edu.au> * Michael Sloan <mgsloan@gmail.com> @@ -32,8 +31,8 @@ * the specific language governing rights and limitations. */ -#ifndef SEEN_SBASIS_H -#define SEEN_SBASIS_H +#ifndef LIB2GEOM_SEEN_SBASIS_H +#define LIB2GEOM_SEEN_SBASIS_H #include <vector> #include <cassert> #include <iostream> @@ -61,13 +60,13 @@ class SBasis : public SBasisN<1>; }; #else -namespace Geom{ +namespace Geom { /** -* \brief S-power basis function class -* -* An empty SBasis is identically 0. */ -class SBasis{ + * @brief Polynomial in symmetric power basis + * @ingroup Fragments + */ +class SBasis { std::vector<Linear> d; void push_back(Linear const&l) { d.push_back(l); } @@ -101,11 +100,17 @@ public: SBasis() {} - explicit SBasis(double a) { - push_back(Linear(a,a)); + explicit SBasis(double a) + : d(1) + { + d[0][0] = a; + d[0][1] = a; } - explicit SBasis(double a, double b) { - push_back(Linear(a,b)); + explicit SBasis(double a, double b) + : d(1) + { + d[0][0] = a; + d[0][1] = b; } SBasis(SBasis const & a) : d(a.d) @@ -117,10 +122,69 @@ public: push_back(bo); } SBasis(Linear* bo) { - push_back(*bo); + if (bo) { + push_back(*bo); + } } explicit SBasis(size_t n, Linear const&l) : d(n, l) {} + SBasis(Coord c0, Coord c1, Coord c2, Coord c3) + : d(2) + { + d[0][0] = c0; + d[1][0] = c1; + d[1][1] = c2; + d[0][1] = c3; + } + SBasis(Coord c0, Coord c1, Coord c2, Coord c3, Coord c4, Coord c5) + : d(3) + { + d[0][0] = c0; + d[1][0] = c1; + d[2][0] = c2; + d[2][1] = c3; + d[1][1] = c4; + d[0][1] = c5; + } + SBasis(Coord c0, Coord c1, Coord c2, Coord c3, Coord c4, Coord c5, + Coord c6, Coord c7) + : d(4) + { + d[0][0] = c0; + d[1][0] = c1; + d[2][0] = c2; + d[3][0] = c3; + d[3][1] = c4; + d[2][1] = c5; + d[1][1] = c6; + d[0][1] = c7; + } + SBasis(Coord c0, Coord c1, Coord c2, Coord c3, Coord c4, Coord c5, + Coord c6, Coord c7, Coord c8, Coord c9) + : d(5) + { + d[0][0] = c0; + d[1][0] = c1; + d[2][0] = c2; + d[3][0] = c3; + d[4][0] = c4; + d[4][1] = c5; + d[3][1] = c6; + d[2][1] = c7; + d[1][1] = c8; + d[0][1] = c9; + } + + // construct from a sequence of coefficients + template <typename Iter> + SBasis(Iter first, Iter last) { + assert(std::distance(first, last) % 2 == 0); + for (; first != last; ++first) { + --last; + push_back(Linear(*first, *last)); + } + } + //IMPL: FragmentConcept typedef double output_type; inline bool isZero(double eps=EPSILON) const { @@ -140,12 +204,10 @@ public: } bool isFinite() const; - inline double at0() const { - if(empty()) return 0; else return (*this)[0][0]; - } - inline double at1() const{ - if(empty()) return 0; else return (*this)[0][1]; - } + inline Coord at0() const { return (*this)[0][0]; } + inline Coord &at0() { return (*this)[0][0]; } + inline Coord at1() const { return (*this)[0][1]; } + inline Coord &at1() { return (*this)[0][1]; } int degreesOfFreedom() const { return size()*2;} @@ -348,8 +410,8 @@ SBasis compose_inverse(SBasis const &f, SBasis const &g, unsigned order=2, doubl \return sbasis \relates SBasis */ -inline SBasis portion(const SBasis &t, double from, double to) { return compose(t, Linear(from, to)); } -inline SBasis portion(const SBasis &t, Interval ivl) { return compose(t, Linear(ivl.min(), ivl.max())); } +SBasis portion(const SBasis &t, double from, double to); +inline SBasis portion(const SBasis &t, Interval const &ivl) { return portion(t, ivl.min(), ivl.max()); } // compute f(g) inline SBasis @@ -364,7 +426,10 @@ inline std::ostream &operator<< (std::ostream &out_file, const Linear &bo) { inline std::ostream &operator<< (std::ostream &out_file, const SBasis & p) { for(unsigned i = 0; i < p.size(); i++) { - out_file << p[i] << "s^" << i << " + "; + if (i != 0) { + out_file << " + "; + } + out_file << p[i] << "s^" << i; } return out_file; } diff --git a/src/2geom/shape.cpp b/src/2geom/shape.cpp deleted file mode 100644 index e9f5e55dc..000000000 --- a/src/2geom/shape.cpp +++ /dev/null @@ -1,689 +0,0 @@ -/** - * \brief Shapes are special paths on which boolops can be performed - * - * Authors: - * Michael G. Sloan <mgsloan@gmail.com> - * Nathan Hurst <njh@mail.csse.monash.edu.au> - * MenTaLguY <mental@rydia.net> - * - * Copyright 2007-2009 Authors - * - * This library is free software; you can redistribute it and/or - * modify it either under the terms of the GNU Lesser General Public - * License version 2.1 as published by the Free Software Foundation - * (the "LGPL") or, at your option, under the terms of the Mozilla - * Public License Version 1.1 (the "MPL"). If you do not alter this - * notice, a recipient may use your version of this file under either - * the MPL or the LGPL. - * - * You should have received a copy of the LGPL along with this library - * in the file COPYING-LGPL-2.1; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * You should have received a copy of the MPL along with this library - * in the file COPYING-MPL-1.1 - * - * The contents of this file are subject to the Mozilla Public License - * Version 1.1 (the "License"); you may not use this file except in - * compliance with the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY - * OF ANY KIND, either express or implied. See the LGPL or the MPL for - * the specific language governing rights and limitations. - * - */ - -#include <2geom/shape.h> - -#include <2geom/utils.h> -#include <2geom/sweep.h> -#include <2geom/ord.h> - -#include <iostream> -#include <algorithm> -#include <cstdlib> - -//#define SHAPE_DEBUG // turns on debug outputting to cout. - -namespace Geom { - -// A little sugar for appending a list to another -template<typename T> -void append(T &a, T const &b) { - a.insert(a.end(), b.begin(), b.end()); -} - -//Orders a list of indices according to their containment within eachother. -struct ContainmentOrder { - std::vector<Region> const *rs; - explicit ContainmentOrder(std::vector<Region> const *r) : rs(r) {} - bool operator()(unsigned a, unsigned b) const { return (*rs)[b].contains((*rs)[a]); } -}; - -//Returns the list of regions containing a particular point. Useful in tandem with ContainmentOrder -std::vector<unsigned> Shape::containment_list(Point p) const { - std::vector<Rect> pnt; - pnt.push_back(Rect(p, p)); - std::vector<std::vector<unsigned> > cull = sweep_bounds(pnt, bounds(*this)); - std::vector<unsigned> containers; - if(cull[0].size() == 0) return containers; - for(unsigned i = 0; i < cull[0].size(); i++) - if(content[cull[0][i]].contains(p)) containers.push_back(cull[0][i]); - return containers; -} - -/* Used within shape_boolean and related functions, as the name describes, finds the - * first false within the list of lists of booleans. - */ -void first_false(std::vector<std::vector<bool> > visited, unsigned &i, unsigned &j) { - for(i = 0, j = 0; i < visited.size(); i++) { - std::vector<bool>::iterator unvisited = std::find(visited[i].begin(), visited[i].end(), false); - if(unvisited != visited[i].end()) { - j = unvisited - visited[i].begin(); - break; - } - } -} - -// Finds a crossing in a list of them, given the sorting index. -unsigned find_crossing(Crossings const &cr, Crossing x, unsigned i) { - return std::lower_bound(cr.begin(), cr.end(), x, CrossingOrder(i)) - cr.begin(); -} - -/* This function handles boolean ops on shapes. The first parameter is a bool - * which determines its behavior in each combination of cases. For proper - * fill information and noncrossing behavior, the fill data of the regions - * must be correct. The boolean parameter determines whether the operation - * is a union or a subtraction. Reversed paths represent inverse regions, - * where everything is included in the fill except for the insides. - * - * Here is a chart of the behavior under various circumstances: - * - * rev = false (union) - * A - * F H - * F A+B -> F A-B -> H - *B - * H B-A -> H AxB -> H - * - * rev = true (intersect) - * A - * F H - * F AxB -> F B-A -> F - *B - * H A-B -> F A+B -> H - * - * F/H = Fill outer / Hole outer - * A/B specify operands - * + = union, - = subtraction, x = intersection - * -> read as "produces" - * - * This is the main function of boolops, yet its operation isn't very complicated. - * It traverses the crossings, and uses the crossing direction to decide whether - * the next segment should be taken from A or from B. The second half of the - * function deals with figuring out what to do with bits that have no intersection. - */ -Shape shape_boolean(bool rev, Shape const & a, Shape const & b, CrossingSet const & crs) { - const Regions ac = a.content, bc = b.content; - - //Keep track of which crossings we've hit. - std::vector<std::vector<bool> > visited; - for(unsigned i = 0; i < crs.size(); i++) - visited.push_back(std::vector<bool>(crs[i].size(), false)); - - //Traverse the crossings, creating chunks - Regions chunks; - while(true) { - unsigned i, j; - first_false(visited, i, j); - if(i == visited.size()) break; - - Path res; - do { - Crossing cur = crs[i][j]; - visited[i][j] = true; - - //get indices of the dual: - unsigned io = cur.getOther(i), jo = find_crossing(crs[io], cur, io); - if(jo < visited[io].size()) visited[io][jo] = true; - - //main driving logic - if(logical_xor(cur.dir, rev)) { - if(i >= ac.size()) { i = io; j = jo; } - j++; - if(j >= crs[i].size()) j = 0; - Crossing next = crs[i][j]; - ac[next.a].boundary.appendPortionTo(res, cur.ta, next.ta); - } else { - if(i < ac.size()) { i = io; j = jo; } - j++; - if(j >= crs[i].size()) j = 0; - Crossing next = crs[i][j]; - bc[next.b - ac.size()].boundary.appendPortionTo(res, cur.tb, next.tb); - } - } while (!visited[i][j]); - if(res.size() > 0) chunks.push_back(Region(res)); - } - - //If true, then we are on the 'subtraction diagonal' - bool const on_sub = logical_xor(a.fill, b.fill); - //If true, outer paths are filled - bool const res_fill = rev ? (on_sub || (a.fill && b.fill)) : (a.fill && b.fill); - - //Handle unintersecting portions - for(unsigned i = 0; i < crs.size(); i++) { - if(crs[i].size() == 0) { - bool env; - bool on_a = i < ac.size(); - Region const & r(on_a ? ac[i] : bc[i - ac.size()]); - Shape const & other(on_a ? b : a); - - std::vector<unsigned> containers = other.containment_list(r.boundary.initialPoint()); - if(containers.empty()) { - //not included in any container, the environment fill is the opposite of the outer fill - env = !res_fill; - if(on_sub && logical_xor(other.fill, res_fill)) env = !env; //If on the subtractor, invert the environment fill - } else { - //environment fill is the same as the inner-most container - std::vector<unsigned>::iterator cit = std::min_element(containers.begin(), containers.end(), ContainmentOrder(&other.content)); - env = other[*cit].isFill(); - } - if(!logical_xor(rev, env)) chunks.push_back(r); //When unioning, environment must be hole for inclusion, when intersecting, it must be filled - } - } - - return Shape(chunks, res_fill); -} - -// Just a convenience wrapper for shape_boolean, which handles the crossings -Shape shape_boolean(bool rev, Shape const & a, Shape const & b) { - CrossingSet crs = crossings_between(a, b); - - return shape_boolean(rev, a, b, crs); -} - - -// Some utility functions for boolop: - -std::vector<double> region_sizes(Shape const &a) { - std::vector<double> ret; - for(unsigned i = 0; i < a.size(); i++) { - ret.push_back(double(a[i].size())); - } - return ret; -} - -Shape shape_boolean_ra(bool rev, Shape const &a, Shape const &b, CrossingSet const &crs) { - return shape_boolean(rev, a.inverse(), b, reverse_ta(crs, a.size(), region_sizes(a))); -} - -Shape shape_boolean_rb(bool rev, Shape const &a, Shape const &b, CrossingSet const &crs) { - return shape_boolean(rev, a, b.inverse(), reverse_tb(crs, a.size(), region_sizes(b))); -} - -/* This is a function based on shape_boolean which allows boolean operations - * to be specified as a logic table. This logic table is 4 bit-flags, which - * correspond to the elements of the 'truth table' for a particular operation. - * These flags are defined with the enums starting with BOOLOP_ . - * - * NOTE: currently doesn't work, as the CrossingSet reversal functions crash - */ -Shape boolop(Shape const &a, Shape const &b, unsigned flags, CrossingSet const &crs) { - THROW_NOTIMPLEMENTED(); - flags &= 15; - if(flags <= BOOLOP_UNION) { - switch(flags) { - case BOOLOP_INTERSECT: return shape_boolean(true, a, b, crs); - case BOOLOP_SUBTRACT_A_B: return shape_boolean_rb(true, a, b, crs); - case BOOLOP_IDENTITY_A: return a; - case BOOLOP_SUBTRACT_B_A: return shape_boolean_ra(true, a, b, crs); - case BOOLOP_IDENTITY_B: return b; - case BOOLOP_EXCLUSION: { - Shape res = shape_boolean_rb(true, a, b, crs); - append(res.content, shape_boolean_ra(true, a, b, crs).content); - return res; - } - case BOOLOP_UNION: return shape_boolean(false, a, b); - } - } else { - flags = ~flags & 15; - switch(flags - BOOLOP_NEITHER) { - case BOOLOP_SUBTRACT_A_B: return shape_boolean_ra(false, a, b, crs); - case BOOLOP_SUBTRACT_B_A: return shape_boolean_rb(false, a, b, crs); - case BOOLOP_EXCLUSION: { - Shape res = shape_boolean_ra(false, a, b, CrossingSet(crs)); - append(res.content, shape_boolean_rb(false, a, b, CrossingSet(crs)).content); - return res; - } - } - return boolop(a, b, flags, crs).inverse(); - } - return Shape(); -} - -/* This version of the boolop function doesn't require a set of crossings, as - * it computes them for you. This is more efficient in some cases, as the - * shape can be inverted before finding crossings. In the special case of - * exclusion it uses the other version of boolop. - */ -Shape boolop(Shape const &a, Shape const &b, unsigned flags) { - flags &= 15; - if(flags <= BOOLOP_UNION) { - switch(flags) { - case BOOLOP_INTERSECT: return shape_boolean(true, a, b); - case BOOLOP_SUBTRACT_A_B: return shape_boolean(true, a, b.inverse()); - case BOOLOP_IDENTITY_A: return a; - case BOOLOP_SUBTRACT_B_A: return shape_boolean(true, b, a.inverse()); - case BOOLOP_IDENTITY_B: return b; - case BOOLOP_EXCLUSION: { - Shape res = shape_boolean(true, a, b.inverse()); - append(res.content, shape_boolean(true, b, a.inverse()).content); - return res; - } //return boolop(a, b, flags, crossings_between(a, b)); - case BOOLOP_UNION: return shape_boolean(false, a, b); - } - } else { - flags = ~flags & 15; - switch(flags) { - case BOOLOP_SUBTRACT_A_B: return shape_boolean(false, b, a.inverse()); - case BOOLOP_SUBTRACT_B_A: return shape_boolean(false, a, b.inverse()); - case BOOLOP_EXCLUSION: { - Shape res = shape_boolean(false, a, b.inverse()); - append(res.content, shape_boolean(false, b, a.inverse()).content); - return res; - } //return boolop(a, b, flags, crossings_between(a, b)); - } - return boolop(a, b, flags).inverse(); - } - return Shape(); -} - -int paths_winding(std::vector<Path> const &ps, Point p) { - int ret = 0; - for(unsigned i = 0; i < ps.size(); i++) - ret += winding(ps[i], p); - return ret; -} - -void add_to_shape(Shape &s, Path const &p, bool fill) { - if(fill) - s.content.push_back(Region(p).asFill()); - else - s.content.push_back(Region(p).asHole()); -} - -int inner_winding(Path const & p, std::vector<Path> const &ps) { - Point pnt = p.initialPoint(); - return paths_winding(ps, pnt) - winding(p, pnt) + 1; -} - -double fudgerize(double d, bool rev) { - double ret = rev ? d - 0.01 : d + 0.01; - if(ret < 0) ret = 0; - return ret; -} - -unsigned pick_coincident(unsigned ix, unsigned jx, bool &rev, std::vector<Path> const &ps, CrossingSet const &crs) { - unsigned ex_jx = jx; - unsigned oix = crs[ix][jx].getOther(ix); - double otime = crs[ix][jx].getTime(oix); - Point cross_point = ps[oix].pointAt(otime), - along = ps[oix].pointAt(fudgerize(otime, rev)) - cross_point, - prev = -along; - bool ex_dir = rev; - for(unsigned k = jx; k < crs[ix].size(); k++) { - unsigned koix = crs[ix][k].getOther(ix); - if(koix == oix) { - if(!are_near(otime, crs[ix][k].getTime(oix))) break; - for(unsigned dir = 0; dir < 2; dir++) { - Point val = ps[ix].pointAt(fudgerize(crs[ix][k].getTime(ix), dir)) - cross_point; - Cmp to_prev = cmp(cross(val, prev), 0); - Cmp from_along = cmp(cross(along, val), 0); - Cmp c = cmp(from_along, to_prev); - if(c == EQUAL_TO && from_along == LESS_THAN) { - ex_jx = k; - prev = val; - ex_dir = dir; - } - } - } - } - rev = ex_dir; - return ex_jx; -} - -unsigned crossing_along(double t, unsigned ix, unsigned jx, bool dir, Crossings const & crs) { - Crossing cur = Crossing(t, t, ix, ix, false); - if(jx < crs.size()) { - double ct = crs[jx].getTime(ix); - if(t == ct) { - cur = crs[jx]; - if(cur.a == cur.b) { - if(jx+1 <= crs.size() && crs[jx+1].getOther(ix) == ix) return jx+1; - if(jx > 0 && crs[jx-1].getOther(ix) == ix) return jx-1; - } - } - } - if(!dir) { - jx = std::upper_bound(crs.begin(), crs.end(), cur, CrossingOrder(ix)) - crs.begin(); - } else { - jx = std::lower_bound(crs.begin(), crs.end(), cur, CrossingOrder(ix)) - crs.begin(); - if(jx == 0) jx = crs.size() - 1; else jx--; - jx = std::lower_bound(crs.begin(), crs.end(), crs[jx], CrossingOrder(ix)) - crs.begin(); - } - if(jx >= crs.size()) jx = 0; - return jx; -} - -void crossing_dual(unsigned &i, unsigned &j, CrossingSet const & crs) { - Crossing cur = crs[i][j]; - i = cur.getOther(i); -#ifdef SHAPE_DEBUG - std::cout << i << "\n"; -#endif - if(crs[i].empty()) - j = 0; - else - j = std::lower_bound(crs[i].begin(), crs[i].end(), cur, CrossingOrder(i)) - crs[i].begin(); -} - -//locate a crossing on the outside, by casting a ray through the middle of the bbox -void outer_crossing(unsigned &ix, unsigned &jx, bool & dir, std::vector<Path> const & ps, CrossingSet const & crs) { - Rect bounds = *(ps[ix].boundsFast()); - double ry = bounds[Y].middle(); - double max_val = bounds.left(), max_t = 0; - ix = ps.size(); - for(unsigned i = 0; i < ps.size(); i++) { - if(!crs[i].empty()) { - std::vector<double> rts = ps[i].roots(ry, Y); - for(unsigned j = 0; j < rts.size(); j++) { - double val = ps[i].valueAt(rts[j], X); - if(val > max_val) { - ix = i; - max_val = val; - max_t = rts[j]; - } - } - } - } - if(ix != ps.size()) { - dir = ps[ix].valueAt(max_t + 0.01, Y) > - ps[ix].valueAt(max_t - 0.01, Y); - jx = crossing_along(max_t, ix, jx, dir, crs[ix]); - } -} - -std::vector<Path> inner_sanitize(std::vector<Path> const & ps) { - CrossingSet crs(crossings_among(ps)); - - Regions chunks; - - std::vector<bool> used_path(ps.size(), false); - std::vector<std::vector<bool> > visited; - for(unsigned i = 0; i < crs.size(); i++) - visited.push_back(std::vector<bool>(crs[i].size(), false)); - - std::vector<Path> result_paths; - - while(true) { - unsigned ix = 0, jx = 0; - bool dir = false; - - //find an outer crossing by trying various paths and checking if the crossings are used - for(; ix < crs.size(); ix++) { - //TODO: optimize so it doesn't unecessarily check stuff - bool cont = true; - for(unsigned j = 0; j < crs[ix].size(); j++) { - if(!visited[ix][j]) { cont = false; break; } - } - if(cont) continue; - unsigned rix = ix, rjx = jx; - outer_crossing(rix, rjx, dir, ps, crs); - if(rix >= crs.size() || visited[rix][rjx]) continue; - ix = rix; jx = rjx; - break; - } - if(ix == crs.size()) break; - crossing_dual(ix, jx, crs); - - dir = !dir; - - Path res; - do { - visited[ix][jx] = true; - //unsigned nix = ix, njx = jx; - //crossing_dual(nix, njx, crs); - //visited[nix][njx] = true; - unsigned fix = ix, fjx = jx; - - bool new_dir = dir; - - jx = crossing_along(crs[ix][jx].getTime(ix), ix, jx, dir, crs[ix]); - if(crs[ix][jx].a != crs[ix][jx].b) crossing_dual(ix, jx, crs); else new_dir = !new_dir; - jx = pick_coincident(ix, jx, new_dir, ps, crs); - - //unsigned nix = ix, njx = jx; - //crossing_dual(nix, njx, crs); - - Crossing from = crs[fix][fjx], - to = crs[ix][jx]; - if(dir) { - // backwards -#ifdef SHAPE_DEBUG - std::cout << "r" << ix << "[" << from.getTime(ix) << ", " << to.getTime(ix) << "]\n"; -#endif - Path p = ps[ix].portion(from.getTime(ix), to.getTime(ix)).reverse(); - for(unsigned i = 0; i < p.size(); i++) - res.append(p[i], Path::STITCH_DISCONTINUOUS); - } else { - // forwards -#ifdef SHAPE_DEBUG - std::cout << "f" << ix << "[" << from.getTime(ix) << ", " << to.getTime(ix) << "]\n"; -#endif - ps[ix].appendPortionTo(res, from.getTime(ix), to.getTime(ix)); - } - dir = new_dir; - } while(!visited[ix][jx]); -#ifdef SHAPE_DEBUG - std::cout << "added " << res.size() << "\n"; -#endif - result_paths.push_back(res); - } - for(unsigned i = 0; i < crs.size(); i++) { - if(crs[i].empty() && !used_path[i]) - result_paths.push_back(ps[i]); - } - return result_paths; -} - -Shape sanitize(std::vector<Path> const & ps) { - std::vector<Path> res; - for(unsigned i = 0; i < ps.size(); i++) { - append(res, inner_sanitize(std::vector<Path>(1, ps[i]))); - } - return stopgap_cleaner(res); -} - -/* WIP sanitizer: -unsigned pick_coincident(unsigned ix, unsigned jx, bool pref, bool &rev, std::vector<Path> const &ps, CrossingSet const &crs) { - unsigned ex_jx = jx; - unsigned oix = crs[ix][jx].getOther(ix); - double otime = crs[ix][jx].getTime(oix); - Point cross_point = ps[oix].pointAt(otime), - along = ps[oix].pointAt(otime + (rev ? -0.01 : 0.01)) - cross_point, - prev = -along; - bool ex_dir = rev; - for(unsigned k = jx; k < crs[ix].size(); k++) { - unsigned koix = crs[ix][k].getOther(ix); - if(koix == oix) { - if(!are_near(otime, crs[ix][k].getTime(oix))) break; - for(unsigned dir = 0; dir < 2; dir++) { - Point val = ps[ix].pointAt(crs[ix][k].getTime(ix) + (dir ? -0.01 : 0.01)) - cross_point; - Cmp to_prev = cmp(cross(val, prev), 0); - Cmp from_along = cmp(cross(along, val), 0); - Cmp c = cmp(from_along, to_prev); - if(c == EQUAL_TO && (from_along == LESS_THAN) == pref) { - ex_jx = k; - prev = val; - ex_dir = dir; - } - } - } - } - rev = ex_dir; - return ex_jx; -} - -unsigned corner_index(unsigned &i) { - div_t div_res = div(i, 4); - i = div_res.quot; - return div_res.rem; -} - -bool corner_direction(unsigned ix, unsigned jc, unsigned corner, CrossingSet const &crs) { - if(crs[ix][jc].a == ix) return corner > 1; else return corner %2 == 1; -} - -Shape sanitize(std::vector<Path> const & ps) { - CrossingSet crs = crossings_among(ps); - - //Keep track of which CORNERS we've hit. - // FF FR RF RR, first is A dir, second B dir - std::vector<std::vector<bool> > visited; - for(unsigned i = 0; i < crs.size(); i++) - visited.push_back(std::vector<bool>(crs[i].size()*4, false)); - - Regions chunks; - while(true) { - unsigned i, j; - first_false(visited, i, j); - unsigned corner = corner_index(j); - - if(i == visited.size()) break; - - bool dir = corner_direction(i, j, corner, crs); - - //Figure out whether we hug the path cw or ccw, based on the orientation of the initial corner: - unsigned oix = crs[i][j].getOther(i); - double otime = crs[i][j].getTime(oix); - bool odir = (oix == crs[i][j].a) ? corner > 1 : corner % 2 == 1; - Point cross_point = ps[oix].pointAt(otime), - along = ps[oix].pointAt(otime + (odir ? -0.01 : 0.01)) - cross_point, - val = ps[i].pointAt(crs[i][j].getTime(i) + (dir ? -0.01 : 0.01)) - cross_point; - - Cmp from_along = cmp(cross(along, val), 0); - bool cw = from_along == LESS_THAN; - std::cout << "cw = " << cw << "\n"; - Path res; - do { - Crossing cur = crs[i][j]; - visited[i][j*4+corner] = true; - - unsigned fix = i, fjx = j; - crossing_dual(i, j, crs); - visited[i][j*4+corner] = true; - i = fix; j = fjx; - - j = crossing_along(crs[i][j].getTime(i), i, j, dir, crs[i]); - - crossing_dual(i, j, crs); - - bool new_dir = dir; - pick_coincident(i, j, cw, new_dir, ps, crs); - - Crossing from = crs[fix][fjx], - to = crs[i][j]; - if(dir) { - // backwards - std::cout << "r" << i << "[" << to.getTime(i) << ", " << from.getTime(i) << "]\n"; - Path p = ps[i].portion(to.getTime(i) + 0.001, from.getTime(i)).reverse(); - for(unsigned k = 0; k < p.size(); k++) - res.append(p[k]); - } else { - // forwards - std::cout << "f" << i << "[" << from.getTime(i) << ", " << to.getTime(i) << "]\n"; - ps[i].appendPortionTo(res, from.getTime(i) + 0.001, to.getTime(i)); - } - if(i == to.a) - corner = (new_dir ? 2 : 0) + (dir ? 1 : 0); - else - corner = (new_dir ? 1 : 0) + (dir ? 2 : 0); - dir = new_dir; - } while(!visited[i][j*4+corner]); - chunks.push_back(Region(res)); -// if(use) { -// chunks.push_back(Region(res, true)); -// } - } - return Shape(chunks); -// return ret; -} */ - -/* This transforms a shape by a matrix. In the case that the matrix flips - * the shape, it reverses the paths in order to preserve the fill. - */ -Shape Shape::operator*(Affine const &m) const { - Shape ret; - for(unsigned i = 0; i < size(); i++) - ret.content.push_back(content[i] * m); - ret.fill = fill; - return ret; -} - -// Inverse is a boolean not, and simply reverses all the paths & fill flags -Shape Shape::inverse() const { - Shape ret; - for(unsigned i = 0; i < size(); i++) - ret.content.push_back(content[i].inverse()); - ret.fill = !fill; - return ret; -} - -bool Shape::contains(Point const &p) const { - std::vector<unsigned> containers = containment_list(p); - if(containers.empty()) return !isFill(); - unsigned ix = *min_element(containers.begin(), containers.end(), ContainmentOrder(&content)); - return content[ix].isFill(); -} - -Shape stopgap_cleaner(std::vector<Path> const &ps) { - if(ps.empty()) return Shape(false); - Shape ret; - for(unsigned i = 0; i < ps.size(); i++) - add_to_shape(ret, ps[i], inner_winding(ps[i], ps) % 2 != 0); - return ret; -} - -bool Shape::inside_invariants() const { //semi-slow & easy to violate - for(unsigned i = 0; i < size(); i++) - if( logical_xor(content[i].isFill(), contains(content[i].boundary.initialPoint())) ) return false; - return true; -} -bool Shape::region_invariants() const { //semi-slow - for(unsigned i = 0; i < size(); i++) - if(!content[i].invariants()) return false; - return true; -} -bool Shape::cross_invariants() const { //slow - CrossingSet crs; // = crossings_among(paths_from_regions(content)); - for(unsigned i = 0; i < crs.size(); i++) - if(!crs[i].empty()) return false; - return true; -} - -bool Shape::invariants() const { - return inside_invariants() && region_invariants() && cross_invariants(); -} - -} - -/* - 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/2geom/shape.h b/src/2geom/shape.h deleted file mode 100644 index 0a7ee9709..000000000 --- a/src/2geom/shape.h +++ /dev/null @@ -1,148 +0,0 @@ -/** - * \brief Shapes are special paths on which boolops can be performed - * - * Authors: - * Michael G. Sloan <mgsloan@gmail.com> - * Nathan Hurst <njh@mail.csse.monash.edu.au> - * MenTaLguY <mental@rydia.net> - * - * Copyright 2007-2009 Authors - * - * This library is free software; you can redistribute it and/or - * modify it either under the terms of the GNU Lesser General Public - * License version 2.1 as published by the Free Software Foundation - * (the "LGPL") or, at your option, under the terms of the Mozilla - * Public License Version 1.1 (the "MPL"). If you do not alter this - * notice, a recipient may use your version of this file under either - * the MPL or the LGPL. - * - * You should have received a copy of the LGPL along with this library - * in the file COPYING-LGPL-2.1; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * You should have received a copy of the MPL along with this library - * in the file COPYING-MPL-1.1 - * - * The contents of this file are subject to the Mozilla Public License - * Version 1.1 (the "License"); you may not use this file except in - * compliance with the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY - * OF ANY KIND, either express or implied. See the LGPL or the MPL for - * the specific language governing rights and limitations. - * - */ - -#ifndef __2GEOM_SHAPE_H -#define __2GEOM_SHAPE_H - -#include <vector> -#include <set> - -#include <2geom/region.h> - -//TODO: BBOX optimizations - -namespace Geom { - -enum { - BOOLOP_JUST_A = 1, - BOOLOP_JUST_B = 2, - BOOLOP_BOTH = 4, - BOOLOP_NEITHER = 8 -}; - -enum { - BOOLOP_NULL = 0, - BOOLOP_INTERSECT = BOOLOP_BOTH, - BOOLOP_SUBTRACT_A_B = BOOLOP_JUST_B, - BOOLOP_IDENTITY_A = BOOLOP_JUST_A | BOOLOP_BOTH, - BOOLOP_SUBTRACT_B_A = BOOLOP_JUST_A, - BOOLOP_IDENTITY_B = BOOLOP_JUST_B | BOOLOP_BOTH, - BOOLOP_EXCLUSION = BOOLOP_JUST_A | BOOLOP_JUST_B, - BOOLOP_UNION = BOOLOP_JUST_A | BOOLOP_JUST_B | BOOLOP_BOTH -}; - -class Shape { - Regions content; - mutable bool fill; - //friend Shape shape_region_boolean(bool rev, Shape const & a, Region const & b); - friend CrossingSet crossings_between(Shape const &a, Shape const &b); - friend Shape shape_boolean(bool rev, Shape const &, Shape const &, CrossingSet const &); - friend Shape boolop(Shape const &a, Shape const &b, unsigned); - friend Shape boolop(Shape const &a, Shape const &b, unsigned, CrossingSet const &); - friend void add_to_shape(Shape &s, Path const &p, bool); - public: - Shape() : fill(true) {} - explicit Shape(Region const & r) { - content = Regions(1, r); - fill = r.fill; - } - explicit Shape(Regions const & r) : content(r) { update_fill(); } - explicit Shape(bool f) : fill(f) {} - Shape(Regions const & r, bool f) : content(r), fill(f) {} - - Regions getContent() const { return content; } - bool isFill() const { return fill; } - - unsigned size() const { return content.size(); } - const Region &operator[](unsigned ix) const { return content[ix]; } - - Shape inverse() const; - Shape operator*(Affine const &m) const; - - bool contains(Point const &p) const; - - bool inside_invariants() const; //semi-slow & easy to violate : checks that the insides are inside, the outsides are outside - bool region_invariants() const; //semi-slow : checks for self crossing - bool cross_invariants() const; //slow : checks that everything is disjoint - bool invariants() const; //vera slow (combo, checks the above) - - private: - std::vector<unsigned> containment_list(Point p) const; - void update_fill() const { - unsigned ix = outer_index(content); - if(ix < size()) - fill = content[ix].fill; - else if(size() > 0) - fill = content.front().fill; - else - fill = true; - } -}; - -inline CrossingSet crossings_between(Shape const &a, Shape const &b) { return crossings(paths_from_regions(a.content), paths_from_regions(b.content)); } - -Shape shape_boolean(bool rev, Shape const &, Shape const &, CrossingSet const &); -Shape shape_boolean(bool rev, Shape const &, Shape const &); - -//unsigned pick_coincident(unsigned ix, unsigned jx, bool &rev, std::vector<Path> const &ps, CrossingSet const &crs); -//void outer_crossing(unsigned &ix, unsigned &jx, bool & dir, std::vector<Path> const & ps, CrossingSet const & crs); -void crossing_dual(unsigned &i, unsigned &j, CrossingSet const & crs); -unsigned crossing_along(double t, unsigned ix, unsigned jx, bool dir, Crossings const & crs); - -Shape boolop(Shape const &, Shape const &, unsigned flags); -Shape boolop(Shape const &, Shape const &, unsigned flags, CrossingSet &); - -Shape sanitize(std::vector<Path> const &ps); - -Shape stopgap_cleaner(std::vector<Path> const &ps); - -inline std::vector<Path> desanitize(Shape const & s) { - return paths_from_regions(s.getContent()); -} - -} - -#endif - -/* - 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/2geom/solve-bezier-one-d.cpp b/src/2geom/solve-bezier-one-d.cpp index 3d87d4926..3a25e9808 100644 --- a/src/2geom/solve-bezier-one-d.cpp +++ b/src/2geom/solve-bezier-one-d.cpp @@ -27,7 +27,7 @@ static int SGN(t x) { return (x > 0 ? 1 : (x < 0 ? -1 : 0)); } **/ class Bernsteins{ public: - static const size_t MAX_DEPTH = 22; + static const size_t MAX_DEPTH = 53; size_t degree, N; std::vector<double> &solutions; //std::vector<double> bc; @@ -38,17 +38,9 @@ public: { } - void subdivide(double const *V, - double t, - double *Left, - double *Right); - unsigned control_poly_flat_enough(double const *V); - double horner(const double *b, double t); - - void find_bernstein_roots(double const *w, /* The control points */ unsigned depth, /* The depth of the recursion */ @@ -134,7 +126,7 @@ void Bernsteins::find_bernstein_roots(double const *w, /* The control points */ r = (fs*t - ft*s) / (fs - ft); if (fabs(t-s) < e * fabs(t+s)) break; - double fr = horner(w, r); + double fr = bernstein_value_at(r, w, degree); if (fr * ft > 0) { @@ -191,24 +183,6 @@ void Bernsteins::find_bernstein_roots(double const *w, /* The control points */ delete[] LR; } - -// suggested by Sederberg. -double Bernsteins::horner(const double *b, double t) -{ - double u, tn, tmp; - u = 1.0 - t; - tn = 1.0; - tmp = b[0] * u; - for(size_t i = 1; i < degree; ++i) - { - tn *= t; - tmp = (tmp + tn*bc[i]*b[i]) * u; - } - return (tmp + tn*t*b[degree]); -} - - - #if 0 /* * control_poly_flat_enough : diff --git a/src/2geom/solve-bezier-parametric.cpp b/src/2geom/solve-bezier-parametric.cpp index 9b0feaee4..2fb3f41da 100644 --- a/src/2geom/solve-bezier-parametric.cpp +++ b/src/2geom/solve-bezier-parametric.cpp @@ -1,8 +1,9 @@ -#include <2geom/solver.h> +#include <2geom/bezier.h> #include <2geom/point.h> +#include <2geom/solver.h> #include <algorithm> -namespace Geom{ +namespace Geom { /*** Find the zeros of the parametric function in 2d defined by two beziers X(t), Y(t). The code subdivides until it happy with the linearity of the bezier. This requires an n^2 subdivision for each step, even when there is only one solution. * @@ -14,13 +15,6 @@ namespace Geom{ /* * Forward declarations */ -static Geom::Point -Bezier(Geom::Point const *V, - unsigned degree, - double t, - Geom::Point *Left, - Geom::Point *Right); - unsigned crossing_count(Geom::Point const *V, unsigned degree); static unsigned @@ -74,7 +68,7 @@ find_parametric_bezier_roots(Geom::Point const *w, /* The control points */ // Right[degree+1]; /* control polygons */ std::vector<Geom::Point> Left( degree+1 ), Right(degree+1); - Bezier(w, degree, 0.5, Left.data(), Right.data()); + casteljau_subdivision(0.5, w, Left.data(), Right.data(), degree); total_subs ++; find_parametric_bezier_roots(Left.data(), degree, solutions, depth+1); find_parametric_bezier_roots(Right.data(), degree, solutions, depth+1); @@ -181,43 +175,6 @@ compute_x_intercept(Geom::Point const *V, /* Control points */ return (A[Geom::X]*V[0][Geom::Y] - A[Geom::Y]*V[0][Geom::X]) / -A[Geom::Y]; } - -/* - * Bezier : - * Evaluate a Bezier curve at a particular parameter value - * Fill in control points for resulting sub-curves. - * - */ -static Geom::Point -Bezier(Geom::Point const *V, /* Control pts */ - unsigned degree, /* Degree of bezier curve */ - double t, /* Parameter value */ - Geom::Point *Left, /* RETURN left half ctl pts */ - Geom::Point *Right) /* RETURN right half ctl pts */ -{ - //Geom::Point Vtemp[degree+1][degree+1]; - std::vector<std::vector<Geom::Point> > Vtemp(degree+1); - for ( size_t i = 0; i < degree + 1; ++i ) - Vtemp.reserve(degree+1); - - /* Copy control points */ - std::copy(V, V+degree+1, Vtemp[0].begin()); - - /* Triangle computation */ - for (unsigned i = 1; i <= degree; i++) { - for (unsigned j = 0; j <= degree - i; j++) { - Vtemp[i][j] = lerp(t, Vtemp[i-1][j], Vtemp[i-1][j+1]); - } - } - - for (unsigned j = 0; j <= degree; j++) - Left[j] = Vtemp[j][0]; - for (unsigned j = 0; j <= degree; j++) - Right[j] = Vtemp[degree-j][j]; - - return (Vtemp[degree][0]); -} - }; /* diff --git a/src/2geom/solve-bezier.cpp b/src/2geom/solve-bezier.cpp index adf3c9ac0..4732169cb 100644 --- a/src/2geom/solve-bezier.cpp +++ b/src/2geom/solve-bezier.cpp @@ -37,12 +37,10 @@ public: double *Left, double *Right); - double secant(Bezier bz); + double secant(Bezier const &bz); - double horner(Bezier bz, double t); - - void find_bernstein_roots(Bezier bz, unsigned depth, + void find_bernstein_roots(Bezier const &bz, unsigned depth, double left_t, double right_t); }; @@ -55,27 +53,7 @@ inline std::ostream &operator<< (std::ostream &out_file, const std::vector<T> & return out_file << "]"; } -Bezier subRight(Bezier bz, double t) { - unsigned order = bz.order(); - unsigned N = order+1; - std::valarray<Coord> row(N); - for (unsigned i = 0; i < N; i++) - row[i] = bz[i]; - - // Triangle computation - const double omt = (1-t); - Bezier Right = bz; - Right[order] = row[order]; - for (unsigned i = 1; i < N; i++) { - for (unsigned j = 0; j < N - i; j++) { - row[j] = omt*row[j] + t*row[j+1]; - } - Right[order-i] = row[order-i]; - } - return Right; -} - -void convex_hull_marching(Bezier src_bz, Bezier bz, +void convex_hull_marching(Bezier const &src_bz, Bezier bz, std::vector<double> &solutions, double left_t, double right_t) @@ -111,7 +89,7 @@ void convex_hull_marching(Bezier src_bz, Bezier bz, << " = " << bz(left_bound) << std::endl; double new_left_t = left_bound * (right_t - left_t) + left_t; std::cout << "new_left_t = " << new_left_t << std::endl; - Bezier bzr = subRight(src_bz, new_left_t); + Bezier bzr = portion(src_bz, new_left_t, 1); while(bzr.order() > 0 && bzr[0] == 0) { std::cout << "deflate\n"; bzr = bzr.deflate(); @@ -171,7 +149,7 @@ Bezier::find_bezier_roots(std::vector<double> &solutions, //std::cout << solutions << std::endl; } -void Bernsteins::find_bernstein_roots(Bezier bz, +void Bernsteins::find_bernstein_roots(Bezier const &bz, unsigned depth, double left_t, double right_t) @@ -194,6 +172,11 @@ void Bernsteins::find_bernstein_roots(Bezier bz, old_sign = sign; } } + // if last control point is zero, that counts as crossing too + if (SGN(bz[bz.size()-1]) == 0) { + ++n_crossings; + } + //std::cout << "n_crossings = " << n_crossings << std::endl; if (n_crossings == 0) return; // no solutions here @@ -271,23 +254,7 @@ void Bernsteins::find_bernstein_roots(Bezier bz, } } - -// suggested by Sederberg. -double Bernsteins::horner(Bezier bz, double t) -{ - double u, tn, tmp; - u = 1.0 - t; - tn = 1.0; - tmp = bz.at0() * u; - for(size_t i = 1; i < bz.degree(); ++i) - { - tn *= t; - tmp = (tmp + tn*choose<double>(bz.order(), (unsigned)i)*bz[i]) * u; - } - return (tmp + tn*t*bz.at1()); -} - -double Bernsteins::secant(Bezier bz) { +double Bernsteins::secant(Bezier const &bz) { double s = 0, t = 1; double e = 1e-14; int side = 0; @@ -303,7 +270,7 @@ double Bernsteins::secant(Bezier bz) { return r; } - double fr = horner(bz, r); + double fr = bz.valueAt(r); if (fr * ft > 0) { diff --git a/src/2geom/solver.h b/src/2geom/solver.h index 793939b2a..5b082cb83 100644 --- a/src/2geom/solver.h +++ b/src/2geom/solver.h @@ -32,16 +32,14 @@ * */ -#ifndef _SOLVE_SBASIS_H -#define _SOLVE_SBASIS_H +#ifndef LIB2GEOM_SEEN_SOLVER_H +#define LIB2GEOM_SEEN_SOLVER_H + #include <2geom/point.h> #include <2geom/sbasis.h> - - #include <vector> - -namespace Geom{ +namespace Geom { class Point; class Bezier; @@ -78,7 +76,6 @@ find_bernstein_roots(std::vector<double> &solutions, /* RETURN candidate t-value double left_t, double right_t); #endif - /* Local Variables: mode:c++ diff --git a/src/2geom/svg-elliptical-arc.h b/src/2geom/svg-elliptical-arc.h deleted file mode 100644 index ba0a18257..000000000 --- a/src/2geom/svg-elliptical-arc.h +++ /dev/null @@ -1,282 +0,0 @@ -/** - * \file - * \brief SVG 1.1-compliant elliptical arc curve - *//* - * Authors: - * MenTaLguY <mental@rydia.net> - * Marco Cecchetti <mrcekets at gmail.com> - * Krzysztof Kosiński <tweenk.pl@gmail.com> - * - * Copyright 2007-2009 Authors - * - * This library is free software; you can redistribute it and/or - * modify it either under the terms of the GNU Lesser General Public - * License version 2.1 as published by the Free Software Foundation - * (the "LGPL") or, at your option, under the terms of the Mozilla - * Public License Version 1.1 (the "MPL"). If you do not alter this - * notice, a recipient may use your version of this file under either - * the MPL or the LGPL. - * - * You should have received a copy of the LGPL along with this library - * in the file COPYING-LGPL-2.1; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * You should have received a copy of the MPL along with this library - * in the file COPYING-MPL-1.1 - * - * The contents of this file are subject to the Mozilla Public License - * Version 1.1 (the "License"); you may not use this file except in - * compliance with the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY - * OF ANY KIND, either express or implied. See the LGPL or the MPL for - * the specific language governing rights and limitations. - */ - - -#ifndef LIB2GEOM_SEEN_SVG_ELLIPTICAL_ARC_H -#define LIB2GEOM_SEEN_SVG_ELLIPTICAL_ARC_H - -#include <2geom/curve.h> -#include <2geom/angle.h> -#include <2geom/utils.h> -#include <2geom/bezier-curve.h> -#include <2geom/elliptical-arc.h> -#include <2geom/sbasis-curve.h> // for non-native methods -#include <2geom/numeric/vector.h> -#include <2geom/numeric/fitting-tool.h> -#include <2geom/numeric/fitting-model.h> -#include <algorithm> - -namespace Geom { - -class SVGEllipticalArc : public EllipticalArc { -public: - SVGEllipticalArc() - : EllipticalArc() - {} - SVGEllipticalArc( Point ip, double rx, double ry, - double rot_angle, bool large_arc, bool sweep, - Point fp - ) - : EllipticalArc() - { - _initial_point = ip; - _final_point = fp; - _rays[X] = rx; _rays[Y] = ry; - _rot_angle = rot_angle; - _large_arc = large_arc; - _sweep = sweep; - _updateCenterAndAngles(true); - } - -#ifndef DOXYGEN_SHOULD_SKIP_THIS - virtual Curve *duplicate() const { - return new SVGEllipticalArc(*this); - } - virtual Coord valueAt(Coord t, Dim2 d) const { - if (isDegenerate()) return chord().valueAt(t, d); - return EllipticalArc::valueAt(t, d); - } - virtual Point pointAt(Coord t) const { - if (isDegenerate()) return chord().pointAt(t); - return EllipticalArc::pointAt(t); - } - virtual std::vector<Point> pointAndDerivatives(Coord t, unsigned int n) const { - if (isDegenerate()) return chord().pointAndDerivatives(t, n); - return EllipticalArc::pointAndDerivatives(t, n); - } - virtual Rect boundsExact() const { - if (isDegenerate()) return chord().boundsExact(); - return EllipticalArc::boundsExact(); - } - virtual OptRect boundsLocal(OptInterval const &i, unsigned int deg) const { - if (isDegenerate()) return chord().boundsLocal(i, deg); - return EllipticalArc::boundsLocal(i, deg); - } - - virtual Curve *derivative() const { - if (isDegenerate()) return chord().derivative(); - return EllipticalArc::derivative(); - } - - virtual std::vector<Coord> roots(Coord v, Dim2 d) const { - if (isDegenerate()) return chord().roots(v, d); - return EllipticalArc::roots(v, d); - } -#ifdef HAVE_GSL - virtual std::vector<Coord> allNearestPoints( Point const& p, double from = 0, double to = 1 ) const { - if (isDegenerate()) { - std::vector<Coord> result; - result.push_back(chord().nearestPoint(p, from, to)); - return result; - } - return EllipticalArc::allNearestPoints(p, from, to); - } -#endif - virtual D2<SBasis> toSBasis() const { - if (isDegenerate()) return chord().toSBasis(); - return EllipticalArc::toSBasis(); - } - virtual bool isSVGCompliant() const { return true; } - // TODO move SVG-specific behavior here. -//protected: - //virtual void _updateCenterAndAngles(); -#endif -}; // end class SVGEllipticalArc - -/* - * useful for testing and debugging - */ -template< class charT > -inline -std::basic_ostream<charT> & -operator<< (std::basic_ostream<charT> & os, const SVGEllipticalArc & ea) -{ - os << "{ cx: " << ea.center(X) << ", cy: " << ea.center(Y) - << ", rx: " << ea.ray(X) << ", ry: " << ea.ray(Y) - << ", rot angle: " << decimal_round(rad_to_deg(ea.rotationAngle()),2) - << ", start angle: " << decimal_round(rad_to_deg(ea.initialAngle()),2) - << ", end angle: " << decimal_round(rad_to_deg(ea.finalAngle()),2) - << " }"; - - return os; -} - - - - -// forward declation -namespace detail -{ - struct ellipse_equation; -} - -// TODO this needs to be rewritten and moved to EllipticalArc header -/* - * make_elliptical_arc - * - * convert a parametric polynomial curve given in symmetric power basis form - * into an SVGEllipticalArc type; in order to be successfull the input curve - * has to look like an actual elliptical arc even if a certain tolerance - * is allowed through an ad-hoc parameter. - * The conversion is performed through an interpolation on a certain amount of - * sample points computed on the input curve; - * the interpolation computes the coefficients of the general implicit equation - * of an ellipse (A*X^2 + B*XY + C*Y^2 + D*X + E*Y + F = 0), then from the - * implicit equation we compute the parametric form. - * - */ -class make_elliptical_arc -{ - public: - typedef D2<SBasis> curve_type; - - /* - * constructor - * - * it doesn't execute the conversion but set the input and output parameters - * - * _ea: the output SVGEllipticalArc that will be generated; - * _curve: the input curve to be converted; - * _total_samples: the amount of sample points to be taken - * on the input curve for performing the conversion - * _tolerance: how much likelihood is required between the input curve - * and the generated elliptical arc; the smaller it is the - * the tolerance the higher it is the likelihood. - */ - make_elliptical_arc( EllipticalArc& _ea, - curve_type const& _curve, - unsigned int _total_samples, - double _tolerance ); - - private: - bool bound_exceeded( unsigned int k, detail::ellipse_equation const & ee, - double e1x, double e1y, double e2 ); - - bool check_bound(double A, double B, double C, double D, double E, double F); - - void fit(); - - bool make_elliptiarc(); - - void print_bound_error(unsigned int k) - { - std::cerr - << "tolerance error" << std::endl - << "at point: " << k << std::endl - << "error value: "<< dist_err << std::endl - << "bound: " << dist_bound << std::endl - << "angle error: " << angle_err - << " (" << angle_tol << ")" << std::endl; - } - - public: - /* - * perform the actual conversion - * return true if the conversion is successfull, false on the contrary - */ - bool operator()() - { - // initialize the reference - const NL::Vector & coeff = fitter.result(); - fit(); - if ( !check_bound(1, coeff[0], coeff[1], coeff[2], coeff[3], coeff[4]) ) - return false; - if ( !(make_elliptiarc()) ) return false; - return true; - } - - /* - * you can set a boolean parameter to tell the conversion routine - * if the output elliptical arc has to be svg compliant or not; - * the default value is true - */ - bool svg_compliant_flag() const - { - return svg_compliant; - } - - void svg_compliant_flag(bool _svg_compliant) - { - svg_compliant = _svg_compliant; - } - - private: - EllipticalArc& ea; // output elliptical arc - const curve_type & curve; // input curve - Piecewise<D2<SBasis> > dcurve; // derivative of the input curve - NL::LFMEllipse model; // model used for fitting - // perform the actual fitting task - NL::least_squeares_fitter<NL::LFMEllipse> fitter; - // tolerance: the user-defined tolerance parameter; - // tol_at_extr: the tolerance at end-points automatically computed - // on the value of "tolerance", and usually more strict; - // tol_at_center: tolerance at the center of the ellipse - // angle_tol: tolerance for the angle btw the input curve tangent - // versor and the ellipse normal versor at the sample points - double tolerance, tol_at_extr, tol_at_center, angle_tol; - Point initial_point, final_point; // initial and final end-points - unsigned int N; // total samples - unsigned int last; // N-1 - double partitions; // N-1 - std::vector<Point> p; // sample points - double dist_err, dist_bound, angle_err; - bool svg_compliant; -}; - -} // end namespace Geom - -#endif // LIB2GEOM_SEEN_SVG_ELLIPTICAL_ARC_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/2geom/svg-path-parser.cpp b/src/2geom/svg-path-parser.cpp index 932f95829..b6e6da869 100644 --- a/src/2geom/svg-path-parser.cpp +++ b/src/2geom/svg-path-parser.cpp @@ -1,3 +1,5 @@ + +#line 1 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl" /** * \file * \brief parse SVG path specifications @@ -30,7 +32,7 @@ * */ - +#include <cstdio> #include <cmath> #include <vector> #include <glib.h> @@ -41,229 +43,108 @@ namespace Geom { -namespace { - -class Parser { -public: - Parser(PathSink &sink) : _absolute(false), _sink(sink) {} - - void parse(char const *str) throw(SVGPathParseError); - -private: - bool _absolute; - Point _current; - Point _initial; - Point _cubic_tangent; - Point _quad_tangent; - std::vector<double> _params; - PathSink &_sink; - - void _reset() { - _absolute = false; - _current = _initial = Point(0, 0); - _quad_tangent = _cubic_tangent = Point(0, 0); - _params.clear(); - } - - void _push(double value) { - _params.push_back(value); - } - - double _pop() { - double value = _params.back(); - _params.pop_back(); - return value; - } - - bool _pop_flag() { - return _pop() != 0.0; - } - - double _pop_coord(Geom::Dim2 axis) { - if (_absolute) { - return _pop(); - } else { - return _pop() + _current[axis]; - } - } - - Point _pop_point() { - double y = _pop_coord(Geom::Y); - double x = _pop_coord(Geom::X); - return Point(x, y); - } - - void _moveTo(Point p) { - _quad_tangent = _cubic_tangent = _current = _initial = p; - _sink.moveTo(p); - } - - void _hlineTo(Point p) { - _quad_tangent = _cubic_tangent = _current = p; - _sink.hlineTo(p[Geom::X]); - } - - void _vlineTo(Point p) { - _quad_tangent = _cubic_tangent = _current = p; - _sink.vlineTo(p[Geom::Y]); - } - - void _lineTo(Point p) { - _quad_tangent = _cubic_tangent = _current = p; - _sink.lineTo(p); - } - - void _curveTo(Point c0, Point c1, Point p) { - _quad_tangent = _current = p; - _cubic_tangent = p + ( p - c1 ); - _sink.curveTo(c0, c1, p); - } - - void _quadTo(Point c, Point p) { - _cubic_tangent = _current = p; - _quad_tangent = p + ( p - c ); - _sink.quadTo(c, p); - } - - void _arcTo(double rx, double ry, double angle, - bool large_arc, bool sweep, Point p) - { - if (are_near(_current, p)) { - return; - } - _quad_tangent = _cubic_tangent = _current = p; - _sink.arcTo(rx, ry, angle, large_arc, sweep, p); - } - - void _closePath() { - _quad_tangent = _cubic_tangent = _current = _initial; - _sink.closePath(); - } -}; - +#line 48 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.cpp" static const char _svg_path_actions[] = { 0, 1, 0, 1, 1, 1, 2, 1, - 3, 1, 4, 1, 5, 1, 15, 1, - 16, 2, 1, 0, 2, 1, 6, 2, - 1, 7, 2, 1, 8, 2, 1, 9, - 2, 1, 10, 2, 1, 11, 2, 1, - 12, 2, 1, 13, 2, 1, 14, 2, - 4, 0, 2, 5, 0, 2, 15, 16, - 3, 1, 6, 0, 3, 1, 6, 16, - 3, 1, 7, 0, 3, 1, 7, 16, - 3, 1, 8, 0, 3, 1, 8, 16, - 3, 1, 9, 0, 3, 1, 9, 16, - 3, 1, 10, 0, 3, 1, 10, 16, - 3, 1, 11, 0, 3, 1, 11, 16, - 3, 1, 12, 0, 3, 1, 12, 16, - 3, 1, 13, 0, 3, 1, 13, 16, - 3, 1, 14, 0, 3, 1, 14, 16 - + 3, 1, 4, 1, 5, 1, 15, 2, + 1, 0, 2, 1, 6, 2, 1, 7, + 2, 1, 8, 2, 1, 9, 2, 1, + 10, 2, 1, 11, 2, 1, 12, 2, + 1, 13, 2, 1, 14, 2, 2, 0, + 2, 3, 0, 2, 4, 0, 2, 5, + 0, 3, 1, 6, 0, 3, 1, 7, + 0, 3, 1, 8, 0, 3, 1, 9, + 0, 3, 1, 10, 0, 3, 1, 11, + 0, 3, 1, 12, 0, 3, 1, 13, + 0, 3, 1, 14, 0 }; static const short _svg_path_key_offsets[] = { - 0, 0, 7, 16, 25, 28, 30, 42, - 52, 55, 57, 90, 121, 124, 126, 138, - 148, 151, 153, 186, 195, 207, 216, 249, - 256, 263, 265, 275, 283, 290, 292, 304, - 314, 317, 319, 328, 335, 341, 346, 353, - 359, 364, 374, 377, 379, 391, 401, 404, - 406, 437, 466, 476, 480, 482, 490, 499, - 508, 511, 513, 525, 535, 538, 540, 552, - 562, 565, 567, 579, 589, 592, 594, 606, - 616, 619, 621, 633, 643, 646, 648, 681, - 712, 724, 733, 745, 754, 766, 775, 787, - 796, 808, 817, 850, 854, 856, 887, 896, - 905, 908, 910, 943, 974, 1007, 1011, 1013, - 1044, 1053, 1062, 1071, 1074, 1076, 1088, 1098, - 1101, 1103, 1115, 1125, 1128, 1130, 1142, 1152, - 1155, 1157, 1190, 1221, 1233, 1242, 1254, 1263, - 1275, 1284, 1317, 1321, 1323, 1354, 1363, 1372, - 1375, 1377, 1389, 1399, 1402, 1404, 1416, 1426, - 1429, 1431, 1443, 1453, 1456, 1458, 1491, 1522, - 1534, 1543, 1555, 1564, 1576, 1585, 1618, 1622, - 1624, 1655, 1664, 1673, 1676, 1678, 1690, 1700, - 1703, 1705, 1738, 1769, 1781, 1790, 1823, 1827, - 1829, 1860, 1869, 1878, 1881, 1883, 1916, 1947, - 1980, 1984, 1986, 2017, 2042, 2067, 2074, 2083, - 2092, 2101, 2110, 2122, 2131, 2164, 2168, 2170, - 2201, 2210, 2219, 2228, 2237, 2241, 2243, 2253, - 2257, 2259, 2269, 2273, 2275, 2285, 2289, 2291, - 2301, 2305, 2307, 2317, 2321, 2323, 2333, 2337, - 2339, 2349, 2353, 2355, 2365, 2369, 2371, 2381, - 2385, 2387, 2397, 2401, 2403, 2413, 2417, 2419, - 2429, 2433, 2435, 2445, 2449, 2451, 2480, 2511, - 2520, 2524, 2526, 2536, 2548, 2557, 2562, 2567, - 2571, 2573, 2580, 2590, 2599, 2603, 2605, 2615, - 2627, 2631, 2633, 2664, 2668, 2670, 2680 + 0, 0, 9, 18, 21, 23, 35, 45, + 48, 50, 53, 55, 67, 77, 80, 82, + 91, 103, 112, 119, 126, 128, 138, 146, + 153, 155, 167, 177, 180, 182, 191, 198, + 204, 211, 218, 224, 234, 244, 247, 249, + 261, 271, 274, 276, 286, 290, 292, 300, + 309, 318, 321, 323, 335, 345, 348, 350, + 362, 372, 375, 377, 389, 399, 402, 404, + 416, 426, 429, 431, 443, 453, 456, 458, + 470, 479, 491, 500, 512, 521, 533, 542, + 554, 563, 567, 569, 578, 587, 590, 592, + 596, 598, 607, 616, 625, 628, 630, 642, + 652, 655, 657, 669, 679, 682, 684, 696, + 706, 709, 711, 723, 732, 744, 753, 765, + 774, 778, 780, 789, 798, 801, 803, 815, + 825, 828, 830, 842, 852, 855, 857, 869, + 879, 882, 884, 896, 905, 917, 926, 938, + 947, 951, 953, 962, 971, 974, 976, 988, + 998, 1001, 1003, 1015, 1024, 1028, 1030, 1039, + 1048, 1051, 1053, 1057, 1059, 1066, 1075, 1084, + 1093, 1102, 1114, 1123, 1127, 1129, 1138, 1147, + 1156, 1165, 1169, 1171, 1181, 1185, 1187, 1197, + 1201, 1203, 1213, 1217, 1219, 1229, 1233, 1235, + 1245, 1249, 1251, 1261, 1265, 1267, 1277, 1281, + 1283, 1293, 1297, 1299, 1309, 1313, 1315, 1325, + 1329, 1331, 1341, 1345, 1347, 1357, 1361, 1363, + 1373, 1377, 1379, 1388, 1392, 1394, 1404, 1416, + 1425, 1435, 1442, 1446, 1448, 1455, 1465, 1474, + 1478, 1480, 1490, 1502, 1506, 1508, 1512, 1514, + 1524, 1530, 1562, 1592, 1624, 1656, 1686, 1714, + 1746, 1776, 1808, 1838, 1870, 1900, 1932, 1962, + 1994, 2024, 2056, 2086, 2118, 2148, 2180, 2210, + 2242, 2272, 2304, 2334, 2366, 2396, 2428, 2458, + 2482, 2506, 2538, 2568, 2596, 2626 }; static const char _svg_path_trans_keys[] = { - 0, 13, 32, 77, 109, 9, 10, 13, - 32, 43, 45, 46, 9, 10, 48, 57, 13, 32, 43, 45, 46, 9, 10, 48, - 57, 46, 48, 57, 48, 57, 13, 32, - 44, 46, 69, 101, 9, 10, 43, 45, - 48, 57, 13, 32, 44, 46, 9, 10, - 43, 45, 48, 57, 46, 48, 57, 48, - 57, 0, 13, 32, 44, 46, 65, 67, - 69, 72, 76, 77, 81, 83, 84, 86, - 90, 97, 99, 101, 104, 108, 109, 113, - 115, 116, 118, 122, 9, 10, 43, 45, - 48, 57, 0, 13, 32, 44, 46, 65, - 67, 72, 76, 77, 81, 83, 84, 86, - 90, 97, 99, 104, 108, 109, 113, 115, - 116, 118, 122, 9, 10, 43, 45, 48, - 57, 46, 48, 57, 48, 57, 13, 32, - 44, 46, 69, 101, 9, 10, 43, 45, - 48, 57, 13, 32, 44, 46, 9, 10, - 43, 45, 48, 57, 46, 48, 57, 48, - 57, 0, 13, 32, 44, 46, 65, 67, - 69, 72, 76, 77, 81, 83, 84, 86, - 90, 97, 99, 101, 104, 108, 109, 113, - 115, 116, 118, 122, 9, 10, 43, 45, + 57, 13, 32, 43, 45, 46, 9, 10, + 48, 57, 46, 48, 57, 48, 57, 13, + 32, 44, 46, 69, 101, 9, 10, 43, + 45, 48, 57, 13, 32, 44, 46, 9, + 10, 43, 45, 48, 57, 46, 48, 57, + 48, 57, 46, 48, 57, 48, 57, 13, + 32, 44, 46, 69, 101, 9, 10, 43, + 45, 48, 57, 13, 32, 44, 46, 9, + 10, 43, 45, 48, 57, 46, 48, 57, 48, 57, 13, 32, 43, 45, 46, 9, 10, 48, 57, 13, 32, 44, 46, 69, 101, 9, 10, 43, 45, 48, 57, 13, 32, 43, 45, 46, 9, 10, 48, 57, - 0, 13, 32, 44, 46, 65, 67, 69, - 72, 76, 77, 81, 83, 84, 86, 90, - 97, 99, 101, 104, 108, 109, 113, 115, - 116, 118, 122, 9, 10, 43, 45, 48, - 57, 13, 32, 46, 9, 10, 48, 57, - 13, 32, 46, 9, 10, 48, 57, 48, + 13, 32, 46, 9, 10, 48, 57, 13, + 32, 46, 9, 10, 48, 57, 48, 57, + 13, 32, 44, 46, 69, 101, 9, 10, + 48, 57, 13, 32, 44, 46, 9, 10, + 48, 57, 13, 32, 46, 9, 10, 48, + 57, 48, 57, 13, 32, 44, 46, 69, + 101, 9, 10, 43, 45, 48, 57, 13, + 32, 44, 46, 9, 10, 43, 45, 48, + 57, 46, 48, 57, 48, 57, 13, 32, + 44, 69, 101, 9, 10, 48, 57, 13, + 32, 44, 48, 49, 9, 10, 13, 32, + 48, 49, 9, 10, 13, 32, 44, 48, + 49, 9, 10, 13, 32, 44, 48, 49, + 9, 10, 13, 32, 48, 49, 9, 10, + 13, 32, 44, 46, 9, 10, 43, 45, + 48, 57, 13, 32, 44, 46, 9, 10, + 43, 45, 48, 57, 46, 48, 57, 48, 57, 13, 32, 44, 46, 69, 101, 9, - 10, 48, 57, 13, 32, 44, 46, 9, - 10, 48, 57, 13, 32, 46, 9, 10, + 10, 43, 45, 48, 57, 13, 32, 44, + 46, 9, 10, 43, 45, 48, 57, 46, 48, 57, 48, 57, 13, 32, 44, 46, - 69, 101, 9, 10, 43, 45, 48, 57, - 13, 32, 44, 46, 9, 10, 43, 45, - 48, 57, 46, 48, 57, 48, 57, 13, - 32, 44, 69, 101, 9, 10, 48, 57, - 13, 32, 44, 48, 49, 9, 10, 13, - 32, 48, 49, 9, 10, 13, 32, 44, - 9, 10, 13, 32, 44, 48, 49, 9, - 10, 13, 32, 48, 49, 9, 10, 13, - 32, 44, 9, 10, 13, 32, 44, 46, - 9, 10, 43, 45, 48, 57, 46, 48, + 69, 101, 9, 10, 48, 57, 43, 45, + 48, 57, 48, 57, 13, 32, 44, 46, + 9, 10, 48, 57, 13, 32, 43, 45, + 46, 9, 10, 48, 57, 13, 32, 43, + 45, 46, 9, 10, 48, 57, 46, 48, 57, 48, 57, 13, 32, 44, 46, 69, 101, 9, 10, 43, 45, 48, 57, 13, 32, 44, 46, 9, 10, 43, 45, 48, - 57, 46, 48, 57, 48, 57, 0, 13, - 32, 44, 46, 65, 67, 69, 72, 76, - 77, 81, 83, 84, 86, 90, 97, 99, - 101, 104, 108, 109, 113, 115, 116, 118, - 122, 9, 10, 48, 57, 0, 13, 32, - 44, 46, 65, 67, 72, 76, 77, 81, - 83, 84, 86, 90, 97, 99, 104, 108, - 109, 113, 115, 116, 118, 122, 9, 10, - 48, 57, 13, 32, 44, 46, 69, 101, - 9, 10, 48, 57, 43, 45, 48, 57, + 57, 46, 48, 57, 48, 57, 13, 32, + 44, 46, 69, 101, 9, 10, 43, 45, 48, 57, 13, 32, 44, 46, 9, 10, - 48, 57, 13, 32, 43, 45, 46, 9, - 10, 48, 57, 13, 32, 43, 45, 46, - 9, 10, 48, 57, 46, 48, 57, 48, + 43, 45, 48, 57, 46, 48, 57, 48, 57, 13, 32, 44, 46, 69, 101, 9, 10, 43, 45, 48, 57, 13, 32, 44, 46, 9, 10, 43, 45, 48, 57, 46, @@ -276,102 +157,29 @@ static const char _svg_path_trans_keys[] = { 10, 43, 45, 48, 57, 46, 48, 57, 48, 57, 13, 32, 44, 46, 69, 101, 9, 10, 43, 45, 48, 57, 13, 32, - 44, 46, 9, 10, 43, 45, 48, 57, - 46, 48, 57, 48, 57, 13, 32, 44, - 46, 69, 101, 9, 10, 43, 45, 48, - 57, 13, 32, 44, 46, 9, 10, 43, - 45, 48, 57, 46, 48, 57, 48, 57, - 0, 13, 32, 44, 46, 65, 67, 69, - 72, 76, 77, 81, 83, 84, 86, 90, - 97, 99, 101, 104, 108, 109, 113, 115, - 116, 118, 122, 9, 10, 43, 45, 48, - 57, 0, 13, 32, 44, 46, 65, 67, - 72, 76, 77, 81, 83, 84, 86, 90, - 97, 99, 104, 108, 109, 113, 115, 116, - 118, 122, 9, 10, 43, 45, 48, 57, - 13, 32, 44, 46, 69, 101, 9, 10, - 43, 45, 48, 57, 13, 32, 43, 45, - 46, 9, 10, 48, 57, 13, 32, 44, - 46, 69, 101, 9, 10, 43, 45, 48, - 57, 13, 32, 43, 45, 46, 9, 10, - 48, 57, 13, 32, 44, 46, 69, 101, - 9, 10, 43, 45, 48, 57, 13, 32, 43, 45, 46, 9, 10, 48, 57, 13, 32, 44, 46, 69, 101, 9, 10, 43, 45, 48, 57, 13, 32, 43, 45, 46, 9, 10, 48, 57, 13, 32, 44, 46, 69, 101, 9, 10, 43, 45, 48, 57, 13, 32, 43, 45, 46, 9, 10, 48, - 57, 0, 13, 32, 44, 46, 65, 67, - 69, 72, 76, 77, 81, 83, 84, 86, - 90, 97, 99, 101, 104, 108, 109, 113, - 115, 116, 118, 122, 9, 10, 43, 45, - 48, 57, 43, 45, 48, 57, 48, 57, - 0, 13, 32, 44, 46, 65, 67, 72, - 76, 77, 81, 83, 84, 86, 90, 97, - 99, 104, 108, 109, 113, 115, 116, 118, - 122, 9, 10, 43, 45, 48, 57, 13, - 32, 43, 45, 46, 9, 10, 48, 57, - 13, 32, 43, 45, 46, 9, 10, 48, - 57, 46, 48, 57, 48, 57, 0, 13, - 32, 44, 46, 65, 67, 69, 72, 76, - 77, 81, 83, 84, 86, 90, 97, 99, - 101, 104, 108, 109, 113, 115, 116, 118, - 122, 9, 10, 43, 45, 48, 57, 0, - 13, 32, 44, 46, 65, 67, 72, 76, - 77, 81, 83, 84, 86, 90, 97, 99, - 104, 108, 109, 113, 115, 116, 118, 122, - 9, 10, 43, 45, 48, 57, 0, 13, - 32, 44, 46, 65, 67, 69, 72, 76, - 77, 81, 83, 84, 86, 90, 97, 99, - 101, 104, 108, 109, 113, 115, 116, 118, - 122, 9, 10, 43, 45, 48, 57, 43, - 45, 48, 57, 48, 57, 0, 13, 32, - 44, 46, 65, 67, 72, 76, 77, 81, - 83, 84, 86, 90, 97, 99, 104, 108, - 109, 113, 115, 116, 118, 122, 9, 10, - 43, 45, 48, 57, 13, 32, 43, 45, - 46, 9, 10, 48, 57, 13, 32, 43, + 57, 13, 32, 44, 46, 69, 101, 9, + 10, 43, 45, 48, 57, 13, 32, 43, 45, 46, 9, 10, 48, 57, 13, 32, - 43, 45, 46, 9, 10, 48, 57, 46, - 48, 57, 48, 57, 13, 32, 44, 46, - 69, 101, 9, 10, 43, 45, 48, 57, - 13, 32, 44, 46, 9, 10, 43, 45, - 48, 57, 46, 48, 57, 48, 57, 13, - 32, 44, 46, 69, 101, 9, 10, 43, - 45, 48, 57, 13, 32, 44, 46, 9, - 10, 43, 45, 48, 57, 46, 48, 57, - 48, 57, 13, 32, 44, 46, 69, 101, - 9, 10, 43, 45, 48, 57, 13, 32, - 44, 46, 9, 10, 43, 45, 48, 57, - 46, 48, 57, 48, 57, 0, 13, 32, - 44, 46, 65, 67, 69, 72, 76, 77, - 81, 83, 84, 86, 90, 97, 99, 101, - 104, 108, 109, 113, 115, 116, 118, 122, - 9, 10, 43, 45, 48, 57, 0, 13, - 32, 44, 46, 65, 67, 72, 76, 77, - 81, 83, 84, 86, 90, 97, 99, 104, - 108, 109, 113, 115, 116, 118, 122, 9, - 10, 43, 45, 48, 57, 13, 32, 44, - 46, 69, 101, 9, 10, 43, 45, 48, + 44, 46, 69, 101, 9, 10, 43, 45, + 48, 57, 13, 32, 43, 45, 46, 9, + 10, 48, 57, 43, 45, 48, 57, 48, 57, 13, 32, 43, 45, 46, 9, 10, - 48, 57, 13, 32, 44, 46, 69, 101, - 9, 10, 43, 45, 48, 57, 13, 32, - 43, 45, 46, 9, 10, 48, 57, 13, - 32, 44, 46, 69, 101, 9, 10, 43, - 45, 48, 57, 13, 32, 43, 45, 46, - 9, 10, 48, 57, 0, 13, 32, 44, - 46, 65, 67, 69, 72, 76, 77, 81, - 83, 84, 86, 90, 97, 99, 101, 104, - 108, 109, 113, 115, 116, 118, 122, 9, - 10, 43, 45, 48, 57, 43, 45, 48, - 57, 48, 57, 0, 13, 32, 44, 46, - 65, 67, 72, 76, 77, 81, 83, 84, - 86, 90, 97, 99, 104, 108, 109, 113, - 115, 116, 118, 122, 9, 10, 43, 45, 48, 57, 13, 32, 43, 45, 46, 9, - 10, 48, 57, 13, 32, 43, 45, 46, - 9, 10, 48, 57, 46, 48, 57, 48, + 10, 48, 57, 46, 48, 57, 48, 57, + 43, 45, 48, 57, 48, 57, 13, 32, + 43, 45, 46, 9, 10, 48, 57, 13, + 32, 43, 45, 46, 9, 10, 48, 57, + 13, 32, 43, 45, 46, 9, 10, 48, + 57, 46, 48, 57, 48, 57, 13, 32, + 44, 46, 69, 101, 9, 10, 43, 45, + 48, 57, 13, 32, 44, 46, 9, 10, + 43, 45, 48, 57, 46, 48, 57, 48, 57, 13, 32, 44, 46, 69, 101, 9, 10, 43, 45, 48, 57, 13, 32, 44, 46, 9, 10, 43, 45, 48, 57, 46, @@ -380,85 +188,49 @@ static const char _svg_path_trans_keys[] = { 13, 32, 44, 46, 9, 10, 43, 45, 48, 57, 46, 48, 57, 48, 57, 13, 32, 44, 46, 69, 101, 9, 10, 43, - 45, 48, 57, 13, 32, 44, 46, 9, - 10, 43, 45, 48, 57, 46, 48, 57, - 48, 57, 0, 13, 32, 44, 46, 65, - 67, 69, 72, 76, 77, 81, 83, 84, - 86, 90, 97, 99, 101, 104, 108, 109, - 113, 115, 116, 118, 122, 9, 10, 43, - 45, 48, 57, 0, 13, 32, 44, 46, - 65, 67, 72, 76, 77, 81, 83, 84, - 86, 90, 97, 99, 104, 108, 109, 113, - 115, 116, 118, 122, 9, 10, 43, 45, - 48, 57, 13, 32, 44, 46, 69, 101, - 9, 10, 43, 45, 48, 57, 13, 32, - 43, 45, 46, 9, 10, 48, 57, 13, - 32, 44, 46, 69, 101, 9, 10, 43, 45, 48, 57, 13, 32, 43, 45, 46, 9, 10, 48, 57, 13, 32, 44, 46, 69, 101, 9, 10, 43, 45, 48, 57, 13, 32, 43, 45, 46, 9, 10, 48, - 57, 0, 13, 32, 44, 46, 65, 67, - 69, 72, 76, 77, 81, 83, 84, 86, - 90, 97, 99, 101, 104, 108, 109, 113, - 115, 116, 118, 122, 9, 10, 43, 45, - 48, 57, 43, 45, 48, 57, 48, 57, - 0, 13, 32, 44, 46, 65, 67, 72, - 76, 77, 81, 83, 84, 86, 90, 97, - 99, 104, 108, 109, 113, 115, 116, 118, - 122, 9, 10, 43, 45, 48, 57, 13, - 32, 43, 45, 46, 9, 10, 48, 57, - 13, 32, 43, 45, 46, 9, 10, 48, + 57, 13, 32, 44, 46, 69, 101, 9, + 10, 43, 45, 48, 57, 13, 32, 43, + 45, 46, 9, 10, 48, 57, 43, 45, + 48, 57, 48, 57, 13, 32, 43, 45, + 46, 9, 10, 48, 57, 13, 32, 43, + 45, 46, 9, 10, 48, 57, 46, 48, + 57, 48, 57, 13, 32, 44, 46, 69, + 101, 9, 10, 43, 45, 48, 57, 13, + 32, 44, 46, 9, 10, 43, 45, 48, 57, 46, 48, 57, 48, 57, 13, 32, 44, 46, 69, 101, 9, 10, 43, 45, 48, 57, 13, 32, 44, 46, 9, 10, 43, 45, 48, 57, 46, 48, 57, 48, - 57, 0, 13, 32, 44, 46, 65, 67, - 69, 72, 76, 77, 81, 83, 84, 86, - 90, 97, 99, 101, 104, 108, 109, 113, - 115, 116, 118, 122, 9, 10, 43, 45, - 48, 57, 0, 13, 32, 44, 46, 65, - 67, 72, 76, 77, 81, 83, 84, 86, - 90, 97, 99, 104, 108, 109, 113, 115, - 116, 118, 122, 9, 10, 43, 45, 48, + 57, 13, 32, 44, 46, 69, 101, 9, + 10, 43, 45, 48, 57, 13, 32, 44, + 46, 9, 10, 43, 45, 48, 57, 46, + 48, 57, 48, 57, 13, 32, 44, 46, + 69, 101, 9, 10, 43, 45, 48, 57, + 13, 32, 43, 45, 46, 9, 10, 48, 57, 13, 32, 44, 46, 69, 101, 9, 10, 43, 45, 48, 57, 13, 32, 43, - 45, 46, 9, 10, 48, 57, 0, 13, - 32, 44, 46, 65, 67, 69, 72, 76, - 77, 81, 83, 84, 86, 90, 97, 99, - 101, 104, 108, 109, 113, 115, 116, 118, - 122, 9, 10, 43, 45, 48, 57, 43, - 45, 48, 57, 48, 57, 0, 13, 32, - 44, 46, 65, 67, 72, 76, 77, 81, - 83, 84, 86, 90, 97, 99, 104, 108, - 109, 113, 115, 116, 118, 122, 9, 10, - 43, 45, 48, 57, 13, 32, 43, 45, - 46, 9, 10, 48, 57, 13, 32, 43, - 45, 46, 9, 10, 48, 57, 46, 48, - 57, 48, 57, 0, 13, 32, 44, 46, - 65, 67, 69, 72, 76, 77, 81, 83, - 84, 86, 90, 97, 99, 101, 104, 108, - 109, 113, 115, 116, 118, 122, 9, 10, - 43, 45, 48, 57, 0, 13, 32, 44, - 46, 65, 67, 72, 76, 77, 81, 83, - 84, 86, 90, 97, 99, 104, 108, 109, - 113, 115, 116, 118, 122, 9, 10, 43, - 45, 48, 57, 0, 13, 32, 44, 46, - 65, 67, 69, 72, 76, 77, 81, 83, - 84, 86, 90, 97, 99, 101, 104, 108, - 109, 113, 115, 116, 118, 122, 9, 10, - 43, 45, 48, 57, 43, 45, 48, 57, - 48, 57, 0, 13, 32, 44, 46, 65, - 67, 72, 76, 77, 81, 83, 84, 86, - 90, 97, 99, 104, 108, 109, 113, 115, - 116, 118, 122, 9, 10, 43, 45, 48, - 57, 0, 13, 32, 65, 67, 72, 76, - 77, 81, 83, 84, 86, 90, 97, 99, - 104, 108, 109, 113, 115, 116, 118, 122, - 9, 10, 0, 13, 32, 65, 67, 72, - 76, 77, 81, 83, 84, 86, 90, 97, - 99, 104, 108, 109, 113, 115, 116, 118, - 122, 9, 10, 13, 32, 46, 9, 10, + 45, 46, 9, 10, 48, 57, 13, 32, + 44, 46, 69, 101, 9, 10, 43, 45, + 48, 57, 13, 32, 43, 45, 46, 9, + 10, 48, 57, 43, 45, 48, 57, 48, + 57, 13, 32, 43, 45, 46, 9, 10, + 48, 57, 13, 32, 43, 45, 46, 9, + 10, 48, 57, 46, 48, 57, 48, 57, + 13, 32, 44, 46, 69, 101, 9, 10, + 43, 45, 48, 57, 13, 32, 44, 46, + 9, 10, 43, 45, 48, 57, 46, 48, + 57, 48, 57, 13, 32, 44, 46, 69, + 101, 9, 10, 43, 45, 48, 57, 13, + 32, 43, 45, 46, 9, 10, 48, 57, + 43, 45, 48, 57, 48, 57, 13, 32, + 43, 45, 46, 9, 10, 48, 57, 13, + 32, 43, 45, 46, 9, 10, 48, 57, + 46, 48, 57, 48, 57, 43, 45, 48, + 57, 48, 57, 13, 32, 46, 9, 10, 48, 57, 13, 32, 43, 45, 46, 9, 10, 48, 57, 13, 32, 43, 45, 46, 9, 10, 48, 57, 13, 32, 43, 45, @@ -466,15 +238,7 @@ static const char _svg_path_trans_keys[] = { 45, 46, 9, 10, 48, 57, 13, 32, 44, 46, 69, 101, 9, 10, 43, 45, 48, 57, 13, 32, 43, 45, 46, 9, - 10, 48, 57, 0, 13, 32, 44, 46, - 65, 67, 69, 72, 76, 77, 81, 83, - 84, 86, 90, 97, 99, 101, 104, 108, - 109, 113, 115, 116, 118, 122, 9, 10, - 43, 45, 48, 57, 43, 45, 48, 57, - 48, 57, 0, 13, 32, 44, 46, 65, - 67, 72, 76, 77, 81, 83, 84, 86, - 90, 97, 99, 104, 108, 109, 113, 115, - 116, 118, 122, 9, 10, 43, 45, 48, + 10, 48, 57, 43, 45, 48, 57, 48, 57, 13, 32, 43, 45, 46, 9, 10, 48, 57, 13, 32, 43, 45, 46, 9, 10, 48, 57, 13, 32, 43, 45, 46, @@ -506,661 +270,980 @@ static const char _svg_path_trans_keys[] = { 10, 43, 45, 48, 57, 43, 45, 48, 57, 48, 57, 13, 32, 44, 46, 9, 10, 43, 45, 48, 57, 43, 45, 48, - 57, 48, 57, 0, 13, 32, 44, 46, + 57, 48, 57, 13, 32, 43, 45, 46, + 9, 10, 48, 57, 43, 45, 48, 57, + 48, 57, 13, 32, 44, 46, 9, 10, + 43, 45, 48, 57, 13, 32, 44, 46, + 69, 101, 9, 10, 43, 45, 48, 57, + 13, 32, 43, 45, 46, 9, 10, 48, + 57, 13, 32, 44, 46, 9, 10, 43, + 45, 48, 57, 13, 32, 44, 48, 49, + 9, 10, 43, 45, 48, 57, 48, 57, + 13, 32, 44, 9, 10, 48, 57, 13, + 32, 44, 46, 69, 101, 9, 10, 48, + 57, 13, 32, 43, 45, 46, 9, 10, + 48, 57, 43, 45, 48, 57, 48, 57, + 13, 32, 44, 46, 9, 10, 43, 45, + 48, 57, 13, 32, 44, 46, 69, 101, + 9, 10, 43, 45, 48, 57, 43, 45, + 48, 57, 48, 57, 43, 45, 48, 57, + 48, 57, 13, 32, 44, 46, 9, 10, + 43, 45, 48, 57, 13, 32, 77, 109, + 9, 10, 13, 32, 44, 46, 65, 67, + 69, 72, 76, 77, 81, 83, 84, 86, + 90, 97, 99, 101, 104, 108, 109, 113, + 115, 116, 118, 122, 9, 10, 43, 45, + 48, 57, 13, 32, 44, 46, 65, 67, + 72, 76, 77, 81, 83, 84, 86, 90, + 97, 99, 104, 108, 109, 113, 115, 116, + 118, 122, 9, 10, 43, 45, 48, 57, + 13, 32, 44, 46, 65, 67, 69, 72, + 76, 77, 81, 83, 84, 86, 90, 97, + 99, 101, 104, 108, 109, 113, 115, 116, + 118, 122, 9, 10, 43, 45, 48, 57, + 13, 32, 44, 46, 65, 67, 69, 72, + 76, 77, 81, 83, 84, 86, 90, 97, + 99, 101, 104, 108, 109, 113, 115, 116, + 118, 122, 9, 10, 43, 45, 48, 57, + 13, 32, 44, 46, 65, 67, 69, 72, + 76, 77, 81, 83, 84, 86, 90, 97, + 99, 101, 104, 108, 109, 113, 115, 116, + 118, 122, 9, 10, 48, 57, 13, 32, + 44, 46, 65, 67, 72, 76, 77, 81, + 83, 84, 86, 90, 97, 99, 104, 108, + 109, 113, 115, 116, 118, 122, 9, 10, + 48, 57, 13, 32, 44, 46, 65, 67, + 69, 72, 76, 77, 81, 83, 84, 86, + 90, 97, 99, 101, 104, 108, 109, 113, + 115, 116, 118, 122, 9, 10, 43, 45, + 48, 57, 13, 32, 44, 46, 65, 67, + 72, 76, 77, 81, 83, 84, 86, 90, + 97, 99, 104, 108, 109, 113, 115, 116, + 118, 122, 9, 10, 43, 45, 48, 57, + 13, 32, 44, 46, 65, 67, 69, 72, + 76, 77, 81, 83, 84, 86, 90, 97, + 99, 101, 104, 108, 109, 113, 115, 116, + 118, 122, 9, 10, 43, 45, 48, 57, + 13, 32, 44, 46, 65, 67, 72, 76, + 77, 81, 83, 84, 86, 90, 97, 99, + 104, 108, 109, 113, 115, 116, 118, 122, + 9, 10, 43, 45, 48, 57, 13, 32, + 44, 46, 65, 67, 69, 72, 76, 77, + 81, 83, 84, 86, 90, 97, 99, 101, + 104, 108, 109, 113, 115, 116, 118, 122, + 9, 10, 43, 45, 48, 57, 13, 32, + 44, 46, 65, 67, 72, 76, 77, 81, + 83, 84, 86, 90, 97, 99, 104, 108, + 109, 113, 115, 116, 118, 122, 9, 10, + 43, 45, 48, 57, 13, 32, 44, 46, + 65, 67, 69, 72, 76, 77, 81, 83, + 84, 86, 90, 97, 99, 101, 104, 108, + 109, 113, 115, 116, 118, 122, 9, 10, + 43, 45, 48, 57, 13, 32, 44, 46, 65, 67, 72, 76, 77, 81, 83, 84, 86, 90, 97, 99, 104, 108, 109, 113, - 115, 116, 118, 122, 9, 10, 48, 57, - 0, 13, 32, 44, 46, 65, 67, 69, + 115, 116, 118, 122, 9, 10, 43, 45, + 48, 57, 13, 32, 44, 46, 65, 67, + 69, 72, 76, 77, 81, 83, 84, 86, + 90, 97, 99, 101, 104, 108, 109, 113, + 115, 116, 118, 122, 9, 10, 43, 45, + 48, 57, 13, 32, 44, 46, 65, 67, 72, 76, 77, 81, 83, 84, 86, 90, - 97, 99, 101, 104, 108, 109, 113, 115, - 116, 118, 122, 9, 10, 48, 57, 13, - 32, 43, 45, 46, 9, 10, 48, 57, - 43, 45, 48, 57, 48, 57, 13, 32, - 44, 46, 9, 10, 43, 45, 48, 57, - 13, 32, 44, 46, 69, 101, 9, 10, - 43, 45, 48, 57, 13, 32, 43, 45, - 46, 9, 10, 48, 57, 13, 32, 44, - 9, 10, 13, 32, 44, 9, 10, 43, - 45, 48, 57, 48, 57, 13, 32, 44, + 97, 99, 104, 108, 109, 113, 115, 116, + 118, 122, 9, 10, 43, 45, 48, 57, + 13, 32, 44, 46, 65, 67, 69, 72, + 76, 77, 81, 83, 84, 86, 90, 97, + 99, 101, 104, 108, 109, 113, 115, 116, + 118, 122, 9, 10, 43, 45, 48, 57, + 13, 32, 44, 46, 65, 67, 72, 76, + 77, 81, 83, 84, 86, 90, 97, 99, + 104, 108, 109, 113, 115, 116, 118, 122, + 9, 10, 43, 45, 48, 57, 13, 32, + 44, 46, 65, 67, 69, 72, 76, 77, + 81, 83, 84, 86, 90, 97, 99, 101, + 104, 108, 109, 113, 115, 116, 118, 122, + 9, 10, 43, 45, 48, 57, 13, 32, + 44, 46, 65, 67, 72, 76, 77, 81, + 83, 84, 86, 90, 97, 99, 104, 108, + 109, 113, 115, 116, 118, 122, 9, 10, + 43, 45, 48, 57, 13, 32, 44, 46, + 65, 67, 69, 72, 76, 77, 81, 83, + 84, 86, 90, 97, 99, 101, 104, 108, + 109, 113, 115, 116, 118, 122, 9, 10, + 43, 45, 48, 57, 13, 32, 44, 46, + 65, 67, 72, 76, 77, 81, 83, 84, + 86, 90, 97, 99, 104, 108, 109, 113, + 115, 116, 118, 122, 9, 10, 43, 45, + 48, 57, 13, 32, 44, 46, 65, 67, + 69, 72, 76, 77, 81, 83, 84, 86, + 90, 97, 99, 101, 104, 108, 109, 113, + 115, 116, 118, 122, 9, 10, 43, 45, + 48, 57, 13, 32, 44, 46, 65, 67, + 72, 76, 77, 81, 83, 84, 86, 90, + 97, 99, 104, 108, 109, 113, 115, 116, + 118, 122, 9, 10, 43, 45, 48, 57, + 13, 32, 44, 46, 65, 67, 69, 72, + 76, 77, 81, 83, 84, 86, 90, 97, + 99, 101, 104, 108, 109, 113, 115, 116, + 118, 122, 9, 10, 43, 45, 48, 57, + 13, 32, 44, 46, 65, 67, 72, 76, + 77, 81, 83, 84, 86, 90, 97, 99, + 104, 108, 109, 113, 115, 116, 118, 122, + 9, 10, 43, 45, 48, 57, 13, 32, + 44, 46, 65, 67, 69, 72, 76, 77, + 81, 83, 84, 86, 90, 97, 99, 101, + 104, 108, 109, 113, 115, 116, 118, 122, + 9, 10, 43, 45, 48, 57, 13, 32, + 44, 46, 65, 67, 72, 76, 77, 81, + 83, 84, 86, 90, 97, 99, 104, 108, + 109, 113, 115, 116, 118, 122, 9, 10, + 43, 45, 48, 57, 13, 32, 44, 46, + 65, 67, 69, 72, 76, 77, 81, 83, + 84, 86, 90, 97, 99, 101, 104, 108, + 109, 113, 115, 116, 118, 122, 9, 10, + 43, 45, 48, 57, 13, 32, 44, 46, + 65, 67, 72, 76, 77, 81, 83, 84, + 86, 90, 97, 99, 104, 108, 109, 113, + 115, 116, 118, 122, 9, 10, 43, 45, + 48, 57, 13, 32, 65, 67, 72, 76, + 77, 81, 83, 84, 86, 90, 97, 99, + 104, 108, 109, 113, 115, 116, 118, 122, + 9, 10, 13, 32, 65, 67, 72, 76, + 77, 81, 83, 84, 86, 90, 97, 99, + 104, 108, 109, 113, 115, 116, 118, 122, + 9, 10, 13, 32, 44, 46, 65, 67, + 69, 72, 76, 77, 81, 83, 84, 86, + 90, 97, 99, 101, 104, 108, 109, 113, + 115, 116, 118, 122, 9, 10, 43, 45, + 48, 57, 13, 32, 44, 46, 65, 67, + 72, 76, 77, 81, 83, 84, 86, 90, + 97, 99, 104, 108, 109, 113, 115, 116, + 118, 122, 9, 10, 43, 45, 48, 57, + 13, 32, 44, 46, 65, 67, 72, 76, + 77, 81, 83, 84, 86, 90, 97, 99, + 104, 108, 109, 113, 115, 116, 118, 122, 9, 10, 48, 57, 13, 32, 44, 46, - 69, 101, 9, 10, 48, 57, 13, 32, - 43, 45, 46, 9, 10, 48, 57, 43, - 45, 48, 57, 48, 57, 13, 32, 44, - 46, 9, 10, 43, 45, 48, 57, 13, - 32, 44, 46, 69, 101, 9, 10, 43, - 45, 48, 57, 43, 45, 48, 57, 48, - 57, 0, 13, 32, 44, 46, 65, 67, + 65, 67, 69, 72, 76, 77, 81, 83, + 84, 86, 90, 97, 99, 101, 104, 108, + 109, 113, 115, 116, 118, 122, 9, 10, + 48, 57, 13, 32, 44, 46, 65, 67, 72, 76, 77, 81, 83, 84, 86, 90, 97, 99, 104, 108, 109, 113, 115, 116, 118, 122, 9, 10, 43, 45, 48, 57, - 43, 45, 48, 57, 48, 57, 13, 32, - 44, 46, 9, 10, 43, 45, 48, 57, 0 }; static const char _svg_path_single_lengths[] = { + 0, 5, 5, 1, 0, 6, 4, 1, + 0, 1, 0, 6, 4, 1, 0, 5, + 6, 5, 3, 3, 0, 6, 4, 3, + 0, 6, 4, 1, 0, 5, 5, 4, + 5, 5, 4, 4, 4, 1, 0, 6, + 4, 1, 0, 6, 2, 0, 4, 5, + 5, 1, 0, 6, 4, 1, 0, 6, + 4, 1, 0, 6, 4, 1, 0, 6, + 4, 1, 0, 6, 4, 1, 0, 6, + 5, 6, 5, 6, 5, 6, 5, 6, + 5, 2, 0, 5, 5, 1, 0, 2, 0, 5, 5, 5, 1, 0, 6, 4, - 1, 0, 27, 25, 1, 0, 6, 4, - 1, 0, 27, 5, 6, 5, 27, 3, - 3, 0, 6, 4, 3, 0, 6, 4, - 1, 0, 5, 5, 4, 3, 5, 4, - 3, 4, 1, 0, 6, 4, 1, 0, - 27, 25, 6, 2, 0, 4, 5, 5, 1, 0, 6, 4, 1, 0, 6, 4, + 1, 0, 6, 5, 6, 5, 6, 5, + 2, 0, 5, 5, 1, 0, 6, 4, 1, 0, 6, 4, 1, 0, 6, 4, - 1, 0, 6, 4, 1, 0, 27, 25, - 6, 5, 6, 5, 6, 5, 6, 5, - 6, 5, 27, 2, 0, 25, 5, 5, - 1, 0, 27, 25, 27, 2, 0, 25, - 5, 5, 5, 1, 0, 6, 4, 1, - 0, 6, 4, 1, 0, 6, 4, 1, - 0, 27, 25, 6, 5, 6, 5, 6, - 5, 27, 2, 0, 25, 5, 5, 1, - 0, 6, 4, 1, 0, 6, 4, 1, - 0, 6, 4, 1, 0, 27, 25, 6, - 5, 6, 5, 6, 5, 27, 2, 0, - 25, 5, 5, 1, 0, 6, 4, 1, - 0, 27, 25, 6, 5, 27, 2, 0, - 25, 5, 5, 1, 0, 27, 25, 27, - 2, 0, 25, 23, 23, 3, 5, 5, - 5, 5, 6, 5, 27, 2, 0, 25, - 5, 5, 5, 5, 2, 0, 4, 2, + 1, 0, 6, 5, 6, 5, 6, 5, + 2, 0, 5, 5, 1, 0, 6, 4, + 1, 0, 6, 5, 2, 0, 5, 5, + 1, 0, 2, 0, 3, 5, 5, 5, + 5, 6, 5, 2, 0, 5, 5, 5, + 5, 2, 0, 4, 2, 0, 4, 2, 0, 4, 2, 0, 4, 2, 0, 4, 2, 0, 4, 2, 0, 4, 2, 0, 4, 2, 0, 4, 2, 0, 4, 2, 0, 4, 2, 0, 4, 2, 0, 4, - 2, 0, 4, 2, 0, 25, 27, 5, - 2, 0, 4, 6, 5, 3, 3, 2, - 0, 3, 6, 5, 2, 0, 4, 6, - 2, 0, 25, 2, 0, 4, 0 + 2, 0, 5, 2, 0, 4, 6, 5, + 4, 5, 2, 0, 3, 6, 5, 2, + 0, 4, 6, 2, 0, 2, 0, 4, + 4, 26, 24, 26, 26, 26, 24, 26, + 24, 26, 24, 26, 24, 26, 24, 26, + 24, 26, 24, 26, 24, 26, 24, 26, + 24, 26, 24, 26, 24, 26, 24, 22, + 22, 26, 24, 24, 26, 24 }; static const char _svg_path_range_lengths[] = { - 0, 1, 2, 2, 1, 1, 3, 3, + 0, 2, 2, 1, 1, 3, 3, 1, + 1, 1, 1, 3, 3, 1, 1, 2, + 3, 2, 2, 2, 1, 2, 2, 2, + 1, 3, 3, 1, 1, 2, 1, 1, + 1, 1, 1, 3, 3, 1, 1, 3, + 3, 1, 1, 2, 1, 1, 2, 2, + 2, 1, 1, 3, 3, 1, 1, 3, + 3, 1, 1, 3, 3, 1, 1, 3, + 3, 1, 1, 3, 3, 1, 1, 3, + 2, 3, 2, 3, 2, 3, 2, 3, + 2, 1, 1, 2, 2, 1, 1, 1, + 1, 2, 2, 2, 1, 1, 3, 3, 1, 1, 3, 3, 1, 1, 3, 3, 1, 1, 3, 2, 3, 2, 3, 2, - 2, 1, 2, 2, 2, 1, 3, 3, - 1, 1, 2, 1, 1, 1, 1, 1, - 1, 3, 1, 1, 3, 3, 1, 1, - 2, 2, 2, 1, 1, 2, 2, 2, + 1, 1, 2, 2, 1, 1, 3, 3, 1, 1, 3, 3, 1, 1, 3, 3, - 1, 1, 3, 3, 1, 1, 3, 3, - 1, 1, 3, 3, 1, 1, 3, 3, - 3, 2, 3, 2, 3, 2, 3, 2, - 3, 2, 3, 1, 1, 3, 2, 2, - 1, 1, 3, 3, 3, 1, 1, 3, - 2, 2, 2, 1, 1, 3, 3, 1, - 1, 3, 3, 1, 1, 3, 3, 1, - 1, 3, 3, 3, 2, 3, 2, 3, - 2, 3, 1, 1, 3, 2, 2, 1, - 1, 3, 3, 1, 1, 3, 3, 1, - 1, 3, 3, 1, 1, 3, 3, 3, - 2, 3, 2, 3, 2, 3, 1, 1, - 3, 2, 2, 1, 1, 3, 3, 1, - 1, 3, 3, 3, 2, 3, 1, 1, - 3, 2, 2, 1, 1, 3, 3, 3, - 1, 1, 3, 1, 1, 2, 2, 2, - 2, 2, 3, 2, 3, 1, 1, 3, - 2, 2, 2, 2, 1, 1, 3, 1, + 1, 1, 3, 2, 3, 2, 3, 2, + 1, 1, 2, 2, 1, 1, 3, 3, + 1, 1, 3, 2, 1, 1, 2, 2, + 1, 1, 1, 1, 2, 2, 2, 2, + 2, 3, 2, 1, 1, 2, 2, 2, + 2, 1, 1, 3, 1, 1, 3, 1, 1, 3, 1, 1, 3, 1, 1, 3, 1, 1, 3, 1, 1, 3, 1, 1, 3, 1, 1, 3, 1, 1, 3, 1, 1, 3, 1, 1, 3, 1, 1, 3, - 1, 1, 3, 1, 1, 2, 2, 2, - 1, 1, 3, 3, 2, 1, 1, 1, - 1, 2, 2, 2, 1, 1, 3, 3, - 1, 1, 3, 1, 1, 3, 0 + 1, 1, 2, 1, 1, 3, 3, 2, + 3, 1, 1, 1, 2, 2, 2, 1, + 1, 3, 3, 1, 1, 1, 1, 3, + 1, 3, 3, 3, 3, 2, 2, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 1, + 1, 3, 3, 2, 2, 3 }; static const short _svg_path_index_offsets[] = { - 0, 0, 7, 15, 23, 26, 28, 38, - 46, 49, 51, 82, 111, 114, 116, 126, - 134, 137, 139, 170, 178, 188, 196, 227, - 233, 239, 241, 250, 257, 263, 265, 275, - 283, 286, 288, 296, 303, 309, 314, 321, - 327, 332, 340, 343, 345, 355, 363, 366, - 368, 398, 426, 435, 439, 441, 448, 456, - 464, 467, 469, 479, 487, 490, 492, 502, - 510, 513, 515, 525, 533, 536, 538, 548, - 556, 559, 561, 571, 579, 582, 584, 615, - 644, 654, 662, 672, 680, 690, 698, 708, - 716, 726, 734, 765, 769, 771, 800, 808, - 816, 819, 821, 852, 881, 912, 916, 918, - 947, 955, 963, 971, 974, 976, 986, 994, - 997, 999, 1009, 1017, 1020, 1022, 1032, 1040, - 1043, 1045, 1076, 1105, 1115, 1123, 1133, 1141, - 1151, 1159, 1190, 1194, 1196, 1225, 1233, 1241, - 1244, 1246, 1256, 1264, 1267, 1269, 1279, 1287, - 1290, 1292, 1302, 1310, 1313, 1315, 1346, 1375, - 1385, 1393, 1403, 1411, 1421, 1429, 1460, 1464, - 1466, 1495, 1503, 1511, 1514, 1516, 1526, 1534, - 1537, 1539, 1570, 1599, 1609, 1617, 1648, 1652, - 1654, 1683, 1691, 1699, 1702, 1704, 1735, 1764, - 1795, 1799, 1801, 1830, 1855, 1880, 1886, 1894, - 1902, 1910, 1918, 1928, 1936, 1967, 1971, 1973, - 2002, 2010, 2018, 2026, 2034, 2038, 2040, 2048, - 2052, 2054, 2062, 2066, 2068, 2076, 2080, 2082, - 2090, 2094, 2096, 2104, 2108, 2110, 2118, 2122, - 2124, 2132, 2136, 2138, 2146, 2150, 2152, 2160, - 2164, 2166, 2174, 2178, 2180, 2188, 2192, 2194, - 2202, 2206, 2208, 2216, 2220, 2222, 2250, 2280, - 2288, 2292, 2294, 2302, 2312, 2320, 2325, 2330, - 2334, 2336, 2342, 2351, 2359, 2363, 2365, 2373, - 2383, 2387, 2389, 2418, 2422, 2424, 2432 + 0, 0, 8, 16, 19, 21, 31, 39, + 42, 44, 47, 49, 59, 67, 70, 72, + 80, 90, 98, 104, 110, 112, 121, 128, + 134, 136, 146, 154, 157, 159, 167, 174, + 180, 187, 194, 200, 208, 216, 219, 221, + 231, 239, 242, 244, 253, 257, 259, 266, + 274, 282, 285, 287, 297, 305, 308, 310, + 320, 328, 331, 333, 343, 351, 354, 356, + 366, 374, 377, 379, 389, 397, 400, 402, + 412, 420, 430, 438, 448, 456, 466, 474, + 484, 492, 496, 498, 506, 514, 517, 519, + 523, 525, 533, 541, 549, 552, 554, 564, + 572, 575, 577, 587, 595, 598, 600, 610, + 618, 621, 623, 633, 641, 651, 659, 669, + 677, 681, 683, 691, 699, 702, 704, 714, + 722, 725, 727, 737, 745, 748, 750, 760, + 768, 771, 773, 783, 791, 801, 809, 819, + 827, 831, 833, 841, 849, 852, 854, 864, + 872, 875, 877, 887, 895, 899, 901, 909, + 917, 920, 922, 926, 928, 934, 942, 950, + 958, 966, 976, 984, 988, 990, 998, 1006, + 1014, 1022, 1026, 1028, 1036, 1040, 1042, 1050, + 1054, 1056, 1064, 1068, 1070, 1078, 1082, 1084, + 1092, 1096, 1098, 1106, 1110, 1112, 1120, 1124, + 1126, 1134, 1138, 1140, 1148, 1152, 1154, 1162, + 1166, 1168, 1176, 1180, 1182, 1190, 1194, 1196, + 1204, 1208, 1210, 1218, 1222, 1224, 1232, 1242, + 1250, 1258, 1265, 1269, 1271, 1277, 1286, 1294, + 1298, 1300, 1308, 1318, 1322, 1324, 1328, 1330, + 1338, 1344, 1374, 1402, 1432, 1462, 1491, 1518, + 1548, 1576, 1606, 1634, 1664, 1692, 1722, 1750, + 1780, 1808, 1838, 1866, 1896, 1924, 1954, 1982, + 2012, 2040, 2070, 2098, 2128, 2156, 2186, 2214, + 2238, 2262, 2292, 2320, 2347, 2376 }; static const short _svg_path_indicies[] = { - 0, 2, 2, 3, 4, 2, 1, 5, - 5, 6, 6, 7, 5, 8, 1, 9, - 9, 10, 10, 11, 9, 12, 1, 13, - 14, 1, 15, 1, 16, 16, 18, 19, - 20, 20, 16, 17, 15, 1, 21, 21, - 23, 24, 21, 22, 25, 1, 26, 27, - 1, 28, 1, 29, 30, 30, 32, 33, - 34, 35, 36, 37, 38, 39, 40, 41, - 42, 43, 44, 45, 46, 36, 47, 48, - 49, 50, 51, 52, 53, 44, 30, 31, - 28, 1, 0, 54, 54, 56, 57, 59, - 60, 61, 62, 3, 63, 64, 65, 66, - 67, 68, 69, 70, 71, 4, 72, 73, - 74, 75, 67, 54, 55, 58, 1, 76, - 77, 1, 78, 1, 79, 79, 81, 82, - 83, 83, 79, 80, 78, 1, 84, 84, - 86, 87, 84, 85, 88, 1, 89, 90, - 1, 91, 1, 92, 93, 93, 95, 96, - 97, 98, 99, 100, 101, 102, 103, 104, - 105, 106, 107, 108, 109, 99, 110, 111, - 112, 113, 114, 115, 116, 107, 93, 94, - 91, 1, 56, 56, 55, 55, 57, 56, - 58, 1, 79, 79, 81, 78, 83, 83, - 79, 80, 77, 1, 86, 86, 85, 85, - 87, 86, 88, 1, 92, 93, 93, 95, - 91, 97, 98, 99, 100, 101, 102, 103, - 104, 105, 106, 107, 108, 109, 99, 110, - 111, 112, 113, 114, 115, 116, 107, 93, - 94, 90, 1, 117, 117, 118, 117, 119, - 1, 120, 120, 121, 120, 122, 1, 123, - 1, 124, 124, 125, 126, 127, 127, 124, - 123, 1, 128, 128, 129, 130, 128, 131, - 1, 129, 129, 130, 129, 131, 1, 132, - 1, 133, 133, 135, 136, 137, 137, 133, - 134, 132, 1, 138, 138, 140, 141, 138, - 139, 142, 1, 143, 144, 1, 145, 1, - 146, 146, 147, 148, 148, 146, 145, 1, - 149, 149, 150, 151, 152, 149, 1, 150, - 150, 151, 152, 150, 1, 153, 153, 154, - 153, 1, 155, 155, 156, 157, 158, 155, - 1, 156, 156, 157, 158, 156, 1, 159, - 159, 160, 159, 1, 161, 161, 163, 164, - 161, 162, 165, 1, 166, 167, 1, 168, - 1, 169, 169, 171, 172, 173, 173, 169, - 170, 168, 1, 174, 174, 176, 177, 174, - 175, 178, 1, 179, 180, 1, 181, 1, - 182, 183, 183, 184, 185, 186, 187, 188, - 189, 190, 191, 192, 193, 194, 195, 196, - 197, 198, 188, 199, 200, 201, 202, 203, - 204, 205, 196, 183, 181, 1, 0, 206, - 206, 120, 121, 59, 60, 61, 62, 3, - 63, 64, 65, 66, 67, 68, 69, 70, - 71, 4, 72, 73, 74, 75, 67, 206, - 122, 1, 124, 124, 125, 123, 127, 127, - 124, 207, 1, 208, 208, 209, 1, 209, - 1, 124, 124, 125, 126, 124, 209, 1, - 210, 210, 211, 211, 212, 210, 213, 1, - 214, 214, 215, 215, 216, 214, 217, 1, - 218, 219, 1, 220, 1, 221, 221, 223, - 224, 225, 225, 221, 222, 220, 1, 226, - 226, 228, 229, 226, 227, 230, 1, 231, - 232, 1, 233, 1, 234, 234, 236, 237, - 238, 238, 234, 235, 233, 1, 239, 239, - 241, 242, 239, 240, 243, 1, 244, 245, - 1, 246, 1, 247, 247, 249, 250, 251, - 251, 247, 248, 246, 1, 252, 252, 254, - 255, 252, 253, 256, 1, 257, 258, 1, - 259, 1, 260, 260, 262, 263, 264, 264, - 260, 261, 259, 1, 265, 265, 267, 268, - 265, 266, 269, 1, 270, 271, 1, 272, - 1, 273, 273, 275, 276, 277, 277, 273, - 274, 272, 1, 278, 278, 280, 281, 278, - 279, 282, 1, 283, 284, 1, 285, 1, - 286, 287, 287, 289, 290, 291, 292, 293, - 294, 295, 296, 297, 298, 299, 300, 301, - 302, 303, 293, 304, 305, 306, 307, 308, - 309, 310, 301, 287, 288, 285, 1, 0, - 311, 311, 214, 216, 59, 60, 61, 62, - 3, 63, 64, 65, 66, 67, 68, 69, - 70, 71, 4, 72, 73, 74, 75, 67, - 311, 215, 217, 1, 221, 221, 223, 220, - 225, 225, 221, 222, 219, 1, 228, 228, - 227, 227, 229, 228, 230, 1, 234, 234, - 236, 233, 238, 238, 234, 235, 232, 1, - 241, 241, 240, 240, 242, 241, 243, 1, - 247, 247, 249, 246, 251, 251, 247, 248, - 245, 1, 254, 254, 253, 253, 255, 254, - 256, 1, 260, 260, 262, 259, 264, 264, - 260, 261, 258, 1, 267, 267, 266, 266, - 268, 267, 269, 1, 273, 273, 275, 272, - 277, 277, 273, 274, 271, 1, 280, 280, - 279, 279, 281, 280, 282, 1, 286, 287, - 287, 289, 285, 291, 292, 293, 294, 295, - 296, 297, 298, 299, 300, 301, 302, 303, - 293, 304, 305, 306, 307, 308, 309, 310, - 301, 287, 288, 284, 1, 312, 312, 313, - 1, 313, 1, 286, 287, 287, 289, 290, - 291, 292, 294, 295, 296, 297, 298, 299, - 300, 301, 302, 303, 304, 305, 306, 307, - 308, 309, 310, 301, 287, 288, 313, 1, - 314, 314, 315, 315, 316, 314, 317, 1, - 318, 318, 319, 319, 320, 318, 321, 1, - 322, 323, 1, 324, 1, 325, 326, 326, - 328, 329, 330, 331, 332, 333, 334, 335, - 336, 337, 338, 339, 340, 341, 342, 332, - 343, 344, 345, 346, 347, 348, 349, 340, - 326, 327, 324, 1, 0, 350, 350, 318, - 320, 59, 60, 61, 62, 3, 63, 64, - 65, 66, 67, 68, 69, 70, 71, 4, - 72, 73, 74, 75, 67, 350, 319, 321, - 1, 325, 326, 326, 328, 324, 330, 331, - 332, 333, 334, 335, 336, 337, 338, 339, - 340, 341, 342, 332, 343, 344, 345, 346, - 347, 348, 349, 340, 326, 327, 323, 1, - 351, 351, 352, 1, 352, 1, 325, 326, - 326, 328, 329, 330, 331, 333, 334, 335, - 336, 337, 338, 339, 340, 341, 342, 343, - 344, 345, 346, 347, 348, 349, 340, 326, - 327, 352, 1, 353, 353, 354, 354, 355, - 353, 356, 1, 357, 357, 358, 358, 359, - 357, 360, 1, 361, 361, 362, 362, 363, - 361, 364, 1, 365, 366, 1, 367, 1, - 368, 368, 370, 371, 372, 372, 368, 369, - 367, 1, 373, 373, 375, 376, 373, 374, - 377, 1, 378, 379, 1, 380, 1, 381, - 381, 383, 384, 385, 385, 381, 382, 380, - 1, 386, 386, 388, 389, 386, 387, 390, - 1, 391, 392, 1, 393, 1, 394, 394, - 396, 397, 398, 398, 394, 395, 393, 1, - 399, 399, 401, 402, 399, 400, 403, 1, - 404, 405, 1, 406, 1, 407, 408, 408, - 410, 411, 412, 413, 414, 415, 416, 417, - 418, 419, 420, 421, 422, 423, 424, 414, - 425, 426, 427, 428, 429, 430, 431, 422, - 408, 409, 406, 1, 0, 432, 432, 361, - 363, 59, 60, 61, 62, 3, 63, 64, - 65, 66, 67, 68, 69, 70, 71, 4, - 72, 73, 74, 75, 67, 432, 362, 364, - 1, 368, 368, 370, 367, 372, 372, 368, - 369, 366, 1, 375, 375, 374, 374, 376, - 375, 377, 1, 381, 381, 383, 380, 385, - 385, 381, 382, 379, 1, 388, 388, 387, - 387, 389, 388, 390, 1, 394, 394, 396, - 393, 398, 398, 394, 395, 392, 1, 401, - 401, 400, 400, 402, 401, 403, 1, 407, - 408, 408, 410, 406, 412, 413, 414, 415, - 416, 417, 418, 419, 420, 421, 422, 423, - 424, 414, 425, 426, 427, 428, 429, 430, - 431, 422, 408, 409, 405, 1, 433, 433, - 434, 1, 434, 1, 407, 408, 408, 410, - 411, 412, 413, 415, 416, 417, 418, 419, - 420, 421, 422, 423, 424, 425, 426, 427, - 428, 429, 430, 431, 422, 408, 409, 434, - 1, 435, 435, 436, 436, 437, 435, 438, - 1, 439, 439, 440, 440, 441, 439, 442, - 1, 443, 444, 1, 445, 1, 446, 446, - 448, 449, 450, 450, 446, 447, 445, 1, - 451, 451, 453, 454, 451, 452, 455, 1, - 456, 457, 1, 458, 1, 459, 459, 461, - 462, 463, 463, 459, 460, 458, 1, 464, - 464, 466, 467, 464, 465, 468, 1, 469, - 470, 1, 471, 1, 472, 472, 474, 475, - 476, 476, 472, 473, 471, 1, 477, 477, - 479, 480, 477, 478, 481, 1, 482, 483, - 1, 484, 1, 485, 486, 486, 488, 489, - 490, 491, 492, 493, 494, 495, 496, 497, - 498, 499, 500, 501, 502, 492, 503, 504, - 505, 506, 507, 508, 509, 500, 486, 487, - 484, 1, 0, 510, 510, 439, 441, 59, - 60, 61, 62, 3, 63, 64, 65, 66, - 67, 68, 69, 70, 71, 4, 72, 73, - 74, 75, 67, 510, 440, 442, 1, 446, - 446, 448, 445, 450, 450, 446, 447, 444, - 1, 453, 453, 452, 452, 454, 453, 455, - 1, 459, 459, 461, 458, 463, 463, 459, - 460, 457, 1, 466, 466, 465, 465, 467, - 466, 468, 1, 472, 472, 474, 471, 476, - 476, 472, 473, 470, 1, 479, 479, 478, - 478, 480, 479, 481, 1, 485, 486, 486, - 488, 484, 490, 491, 492, 493, 494, 495, - 496, 497, 498, 499, 500, 501, 502, 492, - 503, 504, 505, 506, 507, 508, 509, 500, - 486, 487, 483, 1, 511, 511, 512, 1, - 512, 1, 485, 486, 486, 488, 489, 490, - 491, 493, 494, 495, 496, 497, 498, 499, - 500, 501, 502, 503, 504, 505, 506, 507, - 508, 509, 500, 486, 487, 512, 1, 513, - 513, 514, 514, 515, 513, 516, 1, 517, - 517, 518, 518, 519, 517, 520, 1, 521, - 522, 1, 523, 1, 524, 524, 526, 527, - 528, 528, 524, 525, 523, 1, 529, 529, - 531, 532, 529, 530, 533, 1, 534, 535, - 1, 536, 1, 537, 538, 538, 540, 541, + 0, 0, 2, 2, 3, 0, 4, 1, + 5, 5, 6, 6, 7, 5, 8, 1, + 9, 10, 1, 11, 1, 12, 12, 14, + 15, 16, 16, 12, 13, 11, 1, 17, + 17, 19, 20, 17, 18, 21, 1, 22, + 23, 1, 24, 1, 25, 26, 1, 27, + 1, 28, 28, 30, 31, 32, 32, 28, + 29, 27, 1, 33, 33, 35, 36, 33, + 34, 37, 1, 38, 39, 1, 40, 1, + 41, 41, 42, 42, 43, 41, 44, 1, + 28, 28, 30, 27, 32, 32, 28, 29, + 26, 1, 35, 35, 34, 34, 36, 35, + 37, 1, 45, 45, 46, 45, 47, 1, + 48, 48, 49, 48, 50, 1, 51, 1, + 52, 52, 53, 54, 55, 55, 52, 51, + 1, 56, 56, 57, 58, 56, 59, 1, + 57, 57, 58, 57, 59, 1, 60, 1, + 61, 61, 63, 64, 65, 65, 61, 62, + 60, 1, 66, 66, 68, 69, 66, 67, + 70, 1, 71, 72, 1, 73, 1, 74, + 74, 75, 76, 76, 74, 73, 1, 77, + 77, 78, 79, 80, 77, 1, 78, 78, + 79, 80, 78, 1, 81, 81, 82, 83, + 84, 81, 1, 85, 85, 86, 87, 88, + 85, 1, 86, 86, 87, 88, 86, 1, + 89, 89, 91, 92, 89, 90, 93, 1, + 94, 94, 96, 97, 94, 95, 98, 1, + 99, 100, 1, 101, 1, 102, 102, 104, + 105, 106, 106, 102, 103, 101, 1, 107, + 107, 109, 110, 107, 108, 111, 1, 112, + 113, 1, 114, 1, 52, 52, 53, 51, + 55, 55, 52, 115, 1, 116, 116, 117, + 1, 117, 1, 52, 52, 53, 54, 52, + 117, 1, 118, 118, 119, 119, 120, 118, + 121, 1, 122, 122, 123, 123, 124, 122, + 125, 1, 126, 127, 1, 128, 1, 129, + 129, 131, 132, 133, 133, 129, 130, 128, + 1, 134, 134, 136, 137, 134, 135, 138, + 1, 139, 140, 1, 141, 1, 142, 142, + 144, 145, 146, 146, 142, 143, 141, 1, + 147, 147, 149, 150, 147, 148, 151, 1, + 152, 153, 1, 154, 1, 155, 155, 157, + 158, 159, 159, 155, 156, 154, 1, 160, + 160, 162, 163, 160, 161, 164, 1, 165, + 166, 1, 167, 1, 168, 168, 170, 171, + 172, 172, 168, 169, 167, 1, 173, 173, + 175, 176, 173, 174, 177, 1, 178, 179, + 1, 180, 1, 181, 181, 183, 184, 185, + 185, 181, 182, 180, 1, 186, 186, 188, + 189, 186, 187, 190, 1, 191, 192, 1, + 193, 1, 129, 129, 131, 128, 133, 133, + 129, 130, 127, 1, 136, 136, 135, 135, + 137, 136, 138, 1, 142, 142, 144, 141, + 146, 146, 142, 143, 140, 1, 149, 149, + 148, 148, 150, 149, 151, 1, 155, 155, + 157, 154, 159, 159, 155, 156, 153, 1, + 162, 162, 161, 161, 163, 162, 164, 1, + 168, 168, 170, 167, 172, 172, 168, 169, + 166, 1, 175, 175, 174, 174, 176, 175, + 177, 1, 181, 181, 183, 180, 185, 185, + 181, 182, 179, 1, 188, 188, 187, 187, + 189, 188, 190, 1, 194, 194, 195, 1, + 195, 1, 196, 196, 197, 197, 198, 196, + 199, 1, 200, 200, 201, 201, 202, 200, + 203, 1, 204, 205, 1, 206, 1, 207, + 207, 208, 1, 208, 1, 209, 209, 210, + 210, 211, 209, 212, 1, 213, 213, 214, + 214, 215, 213, 216, 1, 217, 217, 218, + 218, 219, 217, 220, 1, 221, 222, 1, + 223, 1, 224, 224, 226, 227, 228, 228, + 224, 225, 223, 1, 229, 229, 231, 232, + 229, 230, 233, 1, 234, 235, 1, 236, + 1, 237, 237, 239, 240, 241, 241, 237, + 238, 236, 1, 242, 242, 244, 245, 242, + 243, 246, 1, 247, 248, 1, 249, 1, + 250, 250, 252, 253, 254, 254, 250, 251, + 249, 1, 255, 255, 257, 258, 255, 256, + 259, 1, 260, 261, 1, 262, 1, 224, + 224, 226, 223, 228, 228, 224, 225, 222, + 1, 231, 231, 230, 230, 232, 231, 233, + 1, 237, 237, 239, 236, 241, 241, 237, + 238, 235, 1, 244, 244, 243, 243, 245, + 244, 246, 1, 250, 250, 252, 249, 254, + 254, 250, 251, 248, 1, 257, 257, 256, + 256, 258, 257, 259, 1, 263, 263, 264, + 1, 264, 1, 265, 265, 266, 266, 267, + 265, 268, 1, 269, 269, 270, 270, 271, + 269, 272, 1, 273, 274, 1, 275, 1, + 276, 276, 278, 279, 280, 280, 276, 277, + 275, 1, 281, 281, 283, 284, 281, 282, + 285, 1, 286, 287, 1, 288, 1, 289, + 289, 291, 292, 293, 293, 289, 290, 288, + 1, 294, 294, 296, 297, 294, 295, 298, + 1, 299, 300, 1, 301, 1, 302, 302, + 304, 305, 306, 306, 302, 303, 301, 1, + 307, 307, 309, 310, 307, 308, 311, 1, + 312, 313, 1, 314, 1, 276, 276, 278, + 275, 280, 280, 276, 277, 274, 1, 283, + 283, 282, 282, 284, 283, 285, 1, 289, + 289, 291, 288, 293, 293, 289, 290, 287, + 1, 296, 296, 295, 295, 297, 296, 298, + 1, 302, 302, 304, 301, 306, 306, 302, + 303, 300, 1, 309, 309, 308, 308, 310, + 309, 311, 1, 315, 315, 316, 1, 316, + 1, 317, 317, 318, 318, 319, 317, 320, + 1, 321, 321, 322, 322, 323, 321, 324, + 1, 325, 326, 1, 327, 1, 328, 328, + 330, 331, 332, 332, 328, 329, 327, 1, + 333, 333, 335, 336, 333, 334, 337, 1, + 338, 339, 1, 340, 1, 328, 328, 330, + 327, 332, 332, 328, 329, 326, 1, 335, + 335, 334, 334, 336, 335, 337, 1, 341, + 341, 342, 1, 342, 1, 343, 343, 344, + 344, 345, 343, 346, 1, 347, 347, 348, + 348, 349, 347, 350, 1, 351, 352, 1, + 353, 1, 354, 354, 355, 1, 355, 1, + 356, 356, 357, 356, 358, 1, 359, 359, + 360, 360, 361, 359, 362, 1, 363, 363, + 364, 364, 365, 363, 366, 1, 367, 367, + 368, 368, 369, 367, 370, 1, 371, 371, + 372, 372, 373, 371, 374, 1, 12, 12, + 14, 11, 16, 16, 12, 13, 10, 1, + 19, 19, 18, 18, 20, 19, 21, 1, + 375, 375, 376, 1, 376, 1, 377, 377, + 378, 378, 379, 377, 380, 1, 381, 381, + 382, 382, 383, 381, 384, 1, 385, 385, + 386, 386, 387, 385, 388, 1, 389, 389, + 390, 390, 391, 389, 392, 1, 393, 393, + 394, 1, 394, 1, 12, 12, 14, 15, + 12, 13, 394, 1, 395, 395, 396, 1, + 396, 1, 328, 328, 330, 331, 328, 329, + 396, 1, 397, 397, 398, 1, 398, 1, + 302, 302, 304, 305, 302, 303, 398, 1, + 399, 399, 400, 1, 400, 1, 289, 289, + 291, 292, 289, 290, 400, 1, 401, 401, + 402, 1, 402, 1, 276, 276, 278, 279, + 276, 277, 402, 1, 403, 403, 404, 1, + 404, 1, 250, 250, 252, 253, 250, 251, + 404, 1, 405, 405, 406, 1, 406, 1, + 237, 237, 239, 240, 237, 238, 406, 1, + 407, 407, 408, 1, 408, 1, 224, 224, + 226, 227, 224, 225, 408, 1, 409, 409, + 410, 1, 410, 1, 181, 181, 183, 184, + 181, 182, 410, 1, 411, 411, 412, 1, + 412, 1, 168, 168, 170, 171, 168, 169, + 412, 1, 413, 413, 414, 1, 414, 1, + 155, 155, 157, 158, 155, 156, 414, 1, + 415, 415, 416, 1, 416, 1, 142, 142, + 144, 145, 142, 143, 416, 1, 417, 417, + 418, 1, 418, 1, 129, 129, 131, 132, + 129, 130, 418, 1, 419, 419, 420, 1, + 420, 1, 109, 109, 108, 108, 110, 109, + 111, 1, 421, 421, 422, 1, 422, 1, + 102, 102, 104, 105, 102, 103, 422, 1, + 102, 102, 104, 101, 106, 106, 102, 103, + 100, 1, 96, 96, 95, 95, 97, 96, + 98, 1, 423, 423, 425, 426, 423, 424, + 427, 1, 428, 428, 429, 430, 431, 428, + 1, 432, 432, 433, 1, 433, 1, 74, + 74, 75, 74, 433, 1, 74, 74, 75, + 73, 76, 76, 74, 72, 1, 68, 68, + 67, 67, 69, 68, 70, 1, 434, 434, + 435, 1, 435, 1, 61, 61, 63, 64, + 61, 62, 435, 1, 61, 61, 63, 60, + 65, 65, 61, 62, 436, 1, 437, 437, + 438, 1, 438, 1, 439, 439, 440, 1, + 440, 1, 28, 28, 30, 31, 28, 29, + 440, 1, 441, 441, 442, 443, 441, 1, + 444, 444, 446, 447, 448, 449, 450, 451, + 452, 453, 454, 455, 456, 457, 458, 459, + 460, 450, 461, 462, 463, 464, 465, 466, + 467, 458, 444, 445, 24, 1, 468, 468, + 41, 43, 469, 470, 471, 472, 442, 473, + 474, 475, 476, 477, 478, 479, 480, 481, + 443, 482, 483, 484, 485, 477, 468, 42, + 44, 1, 486, 486, 488, 489, 490, 491, + 492, 493, 494, 495, 496, 497, 498, 499, + 500, 501, 502, 492, 503, 504, 505, 506, + 507, 508, 509, 500, 486, 487, 40, 1, + 486, 486, 488, 40, 490, 491, 492, 493, + 494, 495, 496, 497, 498, 499, 500, 501, + 502, 492, 503, 504, 505, 506, 507, 508, + 509, 500, 486, 487, 39, 1, 510, 510, + 511, 512, 513, 514, 515, 516, 517, 518, + 519, 520, 521, 522, 523, 524, 525, 515, + 526, 527, 528, 529, 530, 531, 532, 523, + 510, 114, 1, 533, 533, 48, 49, 469, + 470, 471, 472, 442, 473, 474, 475, 476, + 477, 478, 479, 480, 481, 443, 482, 483, + 484, 485, 477, 533, 50, 1, 534, 534, + 536, 537, 538, 539, 540, 541, 542, 543, + 544, 545, 546, 547, 548, 549, 550, 540, + 551, 552, 553, 554, 555, 556, 557, 548, + 534, 535, 193, 1, 558, 558, 122, 124, + 469, 470, 471, 472, 442, 473, 474, 475, + 476, 477, 478, 479, 480, 481, 443, 482, + 483, 484, 485, 477, 558, 123, 125, 1, + 534, 534, 536, 193, 538, 539, 540, 541, 542, 543, 544, 545, 546, 547, 548, 549, - 550, 551, 552, 553, 554, 544, 555, 556, - 557, 558, 559, 560, 561, 552, 538, 539, - 536, 1, 0, 562, 562, 517, 519, 59, - 60, 61, 62, 3, 63, 64, 65, 66, - 67, 68, 69, 70, 71, 4, 72, 73, - 74, 75, 67, 562, 518, 520, 1, 524, - 524, 526, 523, 528, 528, 524, 525, 522, - 1, 531, 531, 530, 530, 532, 531, 533, - 1, 537, 538, 538, 540, 536, 542, 543, - 544, 545, 546, 547, 548, 549, 550, 551, - 552, 553, 554, 544, 555, 556, 557, 558, - 559, 560, 561, 552, 538, 539, 535, 1, - 563, 563, 564, 1, 564, 1, 537, 538, - 538, 540, 541, 542, 543, 545, 546, 547, - 548, 549, 550, 551, 552, 553, 554, 555, - 556, 557, 558, 559, 560, 561, 552, 538, - 539, 564, 1, 565, 565, 566, 566, 567, - 565, 568, 1, 569, 569, 570, 570, 571, - 569, 572, 1, 573, 574, 1, 575, 1, - 576, 577, 577, 579, 580, 581, 582, 583, - 584, 585, 586, 587, 588, 589, 590, 591, - 592, 593, 583, 594, 595, 596, 597, 598, - 599, 600, 591, 577, 578, 575, 1, 0, - 601, 601, 569, 571, 59, 60, 61, 62, - 3, 63, 64, 65, 66, 67, 68, 69, - 70, 71, 4, 72, 73, 74, 75, 67, - 601, 570, 572, 1, 576, 577, 577, 579, - 575, 581, 582, 583, 584, 585, 586, 587, - 588, 589, 590, 591, 592, 593, 583, 594, - 595, 596, 597, 598, 599, 600, 591, 577, - 578, 574, 1, 602, 602, 603, 1, 603, - 1, 576, 577, 577, 579, 580, 581, 582, - 584, 585, 586, 587, 588, 589, 590, 591, + 550, 540, 551, 552, 553, 554, 555, 556, + 557, 548, 534, 535, 192, 1, 534, 534, + 536, 537, 538, 539, 541, 542, 543, 544, + 545, 546, 547, 548, 549, 550, 551, 552, + 553, 554, 555, 556, 557, 548, 534, 535, + 195, 1, 559, 559, 561, 562, 563, 564, + 565, 566, 567, 568, 569, 570, 571, 572, + 573, 574, 575, 565, 576, 577, 578, 579, + 580, 581, 582, 573, 559, 560, 206, 1, + 583, 583, 200, 202, 469, 470, 471, 472, + 442, 473, 474, 475, 476, 477, 478, 479, + 480, 481, 443, 482, 483, 484, 485, 477, + 583, 201, 203, 1, 559, 559, 561, 206, + 563, 564, 565, 566, 567, 568, 569, 570, + 571, 572, 573, 574, 575, 565, 576, 577, + 578, 579, 580, 581, 582, 573, 559, 560, + 205, 1, 559, 559, 561, 562, 563, 564, + 566, 567, 568, 569, 570, 571, 572, 573, + 574, 575, 576, 577, 578, 579, 580, 581, + 582, 573, 559, 560, 208, 1, 584, 584, + 586, 587, 588, 589, 590, 591, 592, 593, + 594, 595, 596, 597, 598, 599, 600, 590, + 601, 602, 603, 604, 605, 606, 607, 598, + 584, 585, 262, 1, 608, 608, 217, 219, + 469, 470, 471, 472, 442, 473, 474, 475, + 476, 477, 478, 479, 480, 481, 443, 482, + 483, 484, 485, 477, 608, 218, 220, 1, + 584, 584, 586, 262, 588, 589, 590, 591, 592, 593, 594, 595, 596, 597, 598, 599, - 600, 591, 577, 578, 603, 1, 604, 605, - 605, 606, 607, 608, 609, 610, 611, 612, + 600, 590, 601, 602, 603, 604, 605, 606, + 607, 598, 584, 585, 261, 1, 584, 584, + 586, 587, 588, 589, 591, 592, 593, 594, + 595, 596, 597, 598, 599, 600, 601, 602, + 603, 604, 605, 606, 607, 598, 584, 585, + 264, 1, 609, 609, 611, 612, 613, 614, + 615, 616, 617, 618, 619, 620, 621, 622, + 623, 624, 625, 615, 626, 627, 628, 629, + 630, 631, 632, 623, 609, 610, 314, 1, + 633, 633, 269, 271, 469, 470, 471, 472, + 442, 473, 474, 475, 476, 477, 478, 479, + 480, 481, 443, 482, 483, 484, 485, 477, + 633, 270, 272, 1, 609, 609, 611, 314, 613, 614, 615, 616, 617, 618, 619, 620, - 621, 622, 623, 624, 615, 605, 1, 0, - 625, 625, 59, 60, 61, 62, 3, 63, - 64, 65, 66, 67, 68, 69, 70, 71, - 4, 72, 73, 74, 75, 67, 625, 1, - 626, 626, 627, 626, 628, 1, 629, 629, - 630, 630, 631, 629, 632, 1, 633, 633, - 634, 634, 635, 633, 636, 1, 637, 637, - 638, 638, 639, 637, 640, 1, 641, 641, - 642, 642, 643, 641, 644, 1, 16, 16, - 18, 15, 20, 20, 16, 17, 14, 1, - 23, 23, 22, 22, 24, 23, 25, 1, - 29, 30, 30, 32, 28, 34, 35, 36, - 37, 38, 39, 40, 41, 42, 43, 44, - 45, 46, 36, 47, 48, 49, 50, 51, - 52, 53, 44, 30, 31, 27, 1, 645, - 645, 646, 1, 646, 1, 29, 30, 30, - 32, 33, 34, 35, 37, 38, 39, 40, - 41, 42, 43, 44, 45, 46, 47, 48, - 49, 50, 51, 52, 53, 44, 30, 31, - 646, 1, 647, 647, 648, 648, 649, 647, - 650, 1, 651, 651, 652, 652, 653, 651, - 654, 1, 655, 655, 656, 656, 657, 655, - 658, 1, 659, 659, 660, 660, 661, 659, - 662, 1, 663, 663, 664, 1, 664, 1, - 16, 16, 18, 19, 16, 17, 664, 1, - 665, 665, 666, 1, 666, 1, 524, 524, - 526, 527, 524, 525, 666, 1, 667, 667, - 668, 1, 668, 1, 472, 472, 474, 475, - 472, 473, 668, 1, 669, 669, 670, 1, - 670, 1, 459, 459, 461, 462, 459, 460, - 670, 1, 671, 671, 672, 1, 672, 1, - 446, 446, 448, 449, 446, 447, 672, 1, - 673, 673, 674, 1, 674, 1, 394, 394, - 396, 397, 394, 395, 674, 1, 675, 675, - 676, 1, 676, 1, 381, 381, 383, 384, - 381, 382, 676, 1, 677, 677, 678, 1, - 678, 1, 368, 368, 370, 371, 368, 369, - 678, 1, 679, 679, 680, 1, 680, 1, - 273, 273, 275, 276, 273, 274, 680, 1, - 681, 681, 682, 1, 682, 1, 260, 260, - 262, 263, 260, 261, 682, 1, 683, 683, - 684, 1, 684, 1, 247, 247, 249, 250, - 247, 248, 684, 1, 685, 685, 686, 1, - 686, 1, 234, 234, 236, 237, 234, 235, - 686, 1, 687, 687, 688, 1, 688, 1, - 221, 221, 223, 224, 221, 222, 688, 1, - 689, 689, 690, 1, 690, 1, 182, 183, - 183, 184, 185, 186, 187, 189, 190, 191, - 192, 193, 194, 195, 196, 197, 198, 199, - 200, 201, 202, 203, 204, 205, 196, 183, - 690, 1, 182, 183, 183, 184, 181, 186, - 187, 188, 189, 190, 191, 192, 193, 194, - 195, 196, 197, 198, 188, 199, 200, 201, - 202, 203, 204, 205, 196, 183, 180, 1, - 176, 176, 175, 175, 177, 176, 178, 1, - 691, 691, 692, 1, 692, 1, 169, 169, - 171, 172, 169, 170, 692, 1, 169, 169, - 171, 168, 173, 173, 169, 170, 167, 1, - 163, 163, 162, 162, 164, 163, 165, 1, - 693, 693, 694, 693, 1, 695, 695, 696, - 695, 1, 697, 697, 698, 1, 698, 1, - 146, 146, 147, 146, 698, 1, 146, 146, - 147, 145, 148, 148, 146, 144, 1, 140, - 140, 139, 139, 141, 140, 142, 1, 699, - 699, 700, 1, 700, 1, 133, 133, 135, - 136, 133, 134, 700, 1, 133, 133, 135, - 132, 137, 137, 133, 134, 701, 1, 702, - 702, 703, 1, 703, 1, 92, 93, 93, - 95, 96, 97, 98, 100, 101, 102, 103, - 104, 105, 106, 107, 108, 109, 110, 111, - 112, 113, 114, 115, 116, 107, 93, 94, - 703, 1, 704, 704, 705, 1, 705, 1, - 79, 79, 81, 82, 79, 80, 705, 1, - 1, 0 + 621, 622, 623, 624, 625, 615, 626, 627, + 628, 629, 630, 631, 632, 623, 609, 610, + 313, 1, 609, 609, 611, 612, 613, 614, + 616, 617, 618, 619, 620, 621, 622, 623, + 624, 625, 626, 627, 628, 629, 630, 631, + 632, 623, 609, 610, 316, 1, 634, 634, + 636, 637, 638, 639, 640, 641, 642, 643, + 644, 645, 646, 647, 648, 649, 650, 640, + 651, 652, 653, 654, 655, 656, 657, 648, + 634, 635, 340, 1, 658, 658, 321, 323, + 469, 470, 471, 472, 442, 473, 474, 475, + 476, 477, 478, 479, 480, 481, 443, 482, + 483, 484, 485, 477, 658, 322, 324, 1, + 634, 634, 636, 340, 638, 639, 640, 641, + 642, 643, 644, 645, 646, 647, 648, 649, + 650, 640, 651, 652, 653, 654, 655, 656, + 657, 648, 634, 635, 339, 1, 634, 634, + 636, 637, 638, 639, 641, 642, 643, 644, + 645, 646, 647, 648, 649, 650, 651, 652, + 653, 654, 655, 656, 657, 648, 634, 635, + 342, 1, 659, 659, 661, 662, 663, 664, + 665, 666, 667, 668, 669, 670, 671, 672, + 673, 674, 675, 665, 676, 677, 678, 679, + 680, 681, 682, 673, 659, 660, 353, 1, + 683, 683, 347, 349, 469, 470, 471, 472, + 442, 473, 474, 475, 476, 477, 478, 479, + 480, 481, 443, 482, 483, 484, 485, 477, + 683, 348, 350, 1, 659, 659, 661, 353, + 663, 664, 665, 666, 667, 668, 669, 670, + 671, 672, 673, 674, 675, 665, 676, 677, + 678, 679, 680, 681, 682, 673, 659, 660, + 352, 1, 659, 659, 661, 662, 663, 664, + 666, 667, 668, 669, 670, 671, 672, 673, + 674, 675, 676, 677, 678, 679, 680, 681, + 682, 673, 659, 660, 355, 1, 684, 684, + 685, 686, 687, 688, 689, 690, 691, 692, + 693, 694, 695, 696, 697, 698, 699, 700, + 701, 702, 703, 694, 684, 1, 704, 704, + 469, 470, 471, 472, 442, 473, 474, 475, + 476, 477, 478, 479, 480, 481, 443, 482, + 483, 484, 485, 477, 704, 1, 444, 444, + 446, 24, 448, 449, 450, 451, 452, 453, + 454, 455, 456, 457, 458, 459, 460, 450, + 461, 462, 463, 464, 465, 466, 467, 458, + 444, 445, 23, 1, 444, 444, 446, 447, + 448, 449, 451, 452, 453, 454, 455, 456, + 457, 458, 459, 460, 461, 462, 463, 464, + 465, 466, 467, 458, 444, 445, 376, 1, + 510, 510, 511, 512, 513, 514, 516, 517, + 518, 519, 520, 521, 522, 523, 524, 525, + 526, 527, 528, 529, 530, 531, 532, 523, + 510, 420, 1, 510, 510, 511, 114, 513, + 514, 515, 516, 517, 518, 519, 520, 521, + 522, 523, 524, 525, 515, 526, 527, 528, + 529, 530, 531, 532, 523, 510, 113, 1, + 486, 486, 488, 489, 490, 491, 493, 494, + 495, 496, 497, 498, 499, 500, 501, 502, + 503, 504, 505, 506, 507, 508, 509, 500, + 486, 487, 438, 1, 0 }; static const short _svg_path_trans_targs[] = { - 270, 0, 1, 2, 193, 3, 4, 5, - 194, 3, 4, 5, 194, 5, 194, 6, - 7, 8, 195, 9, 204, 7, 8, 195, - 9, 196, 9, 196, 10, 270, 11, 12, - 19, 13, 23, 54, 197, 94, 104, 2, - 105, 133, 161, 177, 187, 189, 190, 191, - 192, 193, 200, 201, 202, 203, 11, 12, - 19, 13, 20, 23, 54, 94, 104, 105, - 133, 161, 177, 187, 189, 190, 191, 192, - 200, 201, 202, 203, 13, 20, 14, 15, - 16, 21, 17, 267, 15, 16, 21, 17, - 22, 17, 22, 18, 270, 11, 12, 19, - 13, 23, 54, 264, 94, 104, 2, 105, - 133, 161, 177, 187, 189, 190, 191, 192, - 193, 200, 201, 202, 203, 24, 25, 50, - 24, 25, 50, 26, 27, 28, 29, 51, - 27, 28, 29, 263, 30, 31, 32, 259, - 33, 260, 31, 32, 259, 33, 258, 33, - 258, 34, 35, 36, 255, 35, 36, 37, - 254, 38, 39, 38, 39, 40, 253, 41, - 252, 41, 42, 252, 43, 251, 43, 251, - 44, 45, 46, 247, 47, 248, 45, 46, - 247, 47, 246, 47, 246, 48, 270, 49, - 24, 25, 23, 54, 243, 94, 104, 2, - 105, 133, 161, 177, 187, 189, 190, 191, - 192, 193, 200, 201, 202, 203, 49, 50, - 52, 53, 55, 56, 57, 80, 55, 56, - 57, 80, 57, 80, 58, 59, 60, 81, - 61, 240, 59, 60, 81, 61, 82, 61, - 82, 62, 63, 64, 83, 65, 237, 63, - 64, 83, 65, 84, 65, 84, 66, 67, - 68, 85, 69, 234, 67, 68, 85, 69, - 86, 69, 86, 70, 71, 72, 87, 73, - 231, 71, 72, 87, 73, 88, 73, 88, - 74, 75, 76, 89, 77, 228, 75, 76, - 89, 77, 90, 77, 90, 78, 270, 79, - 56, 55, 57, 23, 54, 91, 94, 104, - 2, 105, 133, 161, 177, 187, 189, 190, - 191, 192, 193, 200, 201, 202, 203, 79, - 92, 93, 95, 96, 97, 100, 95, 96, - 97, 100, 97, 100, 98, 270, 99, 96, - 95, 97, 23, 54, 101, 94, 104, 2, - 105, 133, 161, 177, 187, 189, 190, 191, - 192, 193, 200, 201, 202, 203, 99, 102, - 103, 19, 12, 13, 20, 106, 107, 108, - 123, 106, 107, 108, 123, 108, 123, 109, - 110, 111, 124, 112, 225, 110, 111, 124, - 112, 125, 112, 125, 113, 114, 115, 126, - 116, 222, 114, 115, 126, 116, 127, 116, - 127, 117, 118, 119, 128, 120, 219, 118, - 119, 128, 120, 129, 120, 129, 121, 270, - 122, 107, 106, 108, 23, 54, 130, 94, - 104, 2, 105, 133, 161, 177, 187, 189, - 190, 191, 192, 193, 200, 201, 202, 203, - 122, 131, 132, 134, 135, 136, 151, 134, - 135, 136, 151, 136, 151, 137, 138, 139, - 152, 140, 216, 138, 139, 152, 140, 153, - 140, 153, 141, 142, 143, 154, 144, 213, - 142, 143, 154, 144, 155, 144, 155, 145, - 146, 147, 156, 148, 210, 146, 147, 156, - 148, 157, 148, 157, 149, 270, 150, 135, - 134, 136, 23, 54, 158, 94, 104, 2, - 105, 133, 161, 177, 187, 189, 190, 191, - 192, 193, 200, 201, 202, 203, 150, 159, - 160, 162, 163, 164, 171, 162, 163, 164, - 171, 164, 171, 165, 166, 167, 172, 168, - 207, 166, 167, 172, 168, 173, 168, 173, - 169, 270, 170, 163, 162, 164, 23, 54, - 174, 94, 104, 2, 105, 133, 161, 177, - 187, 189, 190, 191, 192, 193, 200, 201, - 202, 203, 170, 175, 176, 178, 179, 180, - 183, 178, 179, 180, 183, 180, 183, 181, - 270, 182, 179, 178, 180, 23, 54, 184, - 94, 104, 2, 105, 133, 161, 177, 187, - 189, 190, 191, 192, 193, 200, 201, 202, - 203, 182, 185, 186, 270, 188, 23, 54, - 94, 104, 2, 105, 133, 161, 177, 187, - 189, 190, 191, 192, 193, 200, 201, 202, - 203, 188, 24, 25, 50, 55, 56, 57, - 80, 95, 96, 97, 100, 19, 12, 13, - 20, 3, 4, 5, 194, 198, 199, 106, - 107, 108, 123, 134, 135, 136, 151, 162, - 163, 164, 171, 178, 179, 180, 183, 205, - 206, 208, 209, 211, 212, 214, 215, 217, - 218, 220, 221, 223, 224, 226, 227, 229, - 230, 232, 233, 235, 236, 238, 239, 241, - 242, 244, 245, 249, 250, 41, 252, 38, - 39, 256, 257, 261, 262, 263, 265, 266, - 268, 269 + 2, 0, 3, 4, 161, 2, 3, 4, + 161, 4, 161, 5, 6, 7, 162, 8, + 169, 6, 7, 162, 8, 265, 8, 265, + 233, 10, 16, 11, 12, 13, 17, 14, + 229, 12, 13, 17, 14, 236, 14, 236, + 235, 15, 9, 10, 16, 19, 20, 43, + 19, 20, 43, 21, 22, 23, 24, 44, + 22, 23, 24, 226, 25, 26, 27, 222, + 28, 223, 26, 27, 222, 28, 221, 28, + 221, 29, 30, 31, 218, 30, 31, 32, + 217, 33, 34, 35, 216, 33, 34, 35, + 216, 36, 37, 215, 38, 214, 36, 37, + 215, 38, 214, 38, 214, 39, 40, 41, + 210, 42, 211, 40, 41, 210, 42, 268, + 42, 268, 237, 43, 45, 46, 48, 49, + 50, 71, 48, 49, 50, 71, 50, 71, + 51, 52, 53, 72, 54, 205, 52, 53, + 72, 54, 73, 54, 73, 55, 56, 57, + 74, 58, 202, 56, 57, 74, 58, 75, + 58, 75, 59, 60, 61, 76, 62, 199, + 60, 61, 76, 62, 77, 62, 77, 63, + 64, 65, 78, 66, 196, 64, 65, 78, + 66, 79, 66, 79, 67, 68, 69, 80, + 70, 193, 68, 69, 80, 70, 241, 70, + 241, 239, 82, 242, 84, 85, 86, 245, + 84, 85, 86, 245, 86, 245, 243, 88, + 246, 15, 9, 10, 16, 91, 92, 93, + 106, 91, 92, 93, 106, 93, 106, 94, + 95, 96, 107, 97, 190, 95, 96, 107, + 97, 108, 97, 108, 98, 99, 100, 109, + 101, 187, 99, 100, 109, 101, 110, 101, + 110, 102, 103, 104, 111, 105, 184, 103, + 104, 111, 105, 249, 105, 249, 247, 113, + 250, 115, 116, 117, 130, 115, 116, 117, + 130, 117, 130, 118, 119, 120, 131, 121, + 181, 119, 120, 131, 121, 132, 121, 132, + 122, 123, 124, 133, 125, 178, 123, 124, + 133, 125, 134, 125, 134, 126, 127, 128, + 135, 129, 175, 127, 128, 135, 129, 253, + 129, 253, 251, 137, 254, 139, 140, 141, + 146, 139, 140, 141, 146, 141, 146, 142, + 143, 144, 147, 145, 172, 143, 144, 147, + 145, 257, 145, 257, 255, 149, 258, 151, + 152, 153, 261, 151, 152, 153, 261, 153, + 261, 259, 155, 262, 19, 20, 43, 48, + 49, 50, 71, 84, 85, 86, 245, 15, + 9, 10, 16, 2, 3, 4, 161, 164, + 266, 91, 92, 93, 106, 115, 116, 117, + 130, 139, 140, 141, 146, 151, 152, 153, + 261, 170, 171, 173, 174, 176, 177, 179, + 180, 182, 183, 185, 186, 188, 189, 191, + 192, 194, 195, 197, 198, 200, 201, 203, + 204, 206, 207, 209, 267, 212, 213, 36, + 37, 215, 38, 214, 33, 34, 35, 216, + 219, 220, 224, 225, 226, 228, 269, 230, + 231, 232, 1, 160, 234, 9, 15, 10, + 18, 47, 163, 83, 89, 1, 90, 114, + 138, 150, 263, 156, 157, 158, 159, 160, + 165, 166, 167, 168, 234, 18, 47, 83, + 89, 90, 114, 138, 150, 263, 156, 157, + 158, 159, 165, 166, 167, 168, 234, 9, + 15, 10, 18, 47, 227, 83, 89, 1, + 90, 114, 138, 150, 263, 156, 157, 158, + 159, 160, 165, 166, 167, 168, 238, 19, + 20, 18, 47, 208, 83, 89, 1, 90, + 114, 138, 150, 263, 156, 157, 158, 159, + 160, 165, 166, 167, 168, 238, 240, 49, + 48, 50, 18, 47, 81, 83, 89, 1, + 90, 114, 138, 150, 263, 156, 157, 158, + 159, 160, 165, 166, 167, 168, 240, 244, + 85, 84, 86, 18, 47, 87, 83, 89, + 1, 90, 114, 138, 150, 263, 156, 157, + 158, 159, 160, 165, 166, 167, 168, 244, + 248, 92, 91, 93, 18, 47, 112, 83, + 89, 1, 90, 114, 138, 150, 263, 156, + 157, 158, 159, 160, 165, 166, 167, 168, + 248, 252, 116, 115, 117, 18, 47, 136, + 83, 89, 1, 90, 114, 138, 150, 263, + 156, 157, 158, 159, 160, 165, 166, 167, + 168, 252, 256, 140, 139, 141, 18, 47, + 148, 83, 89, 1, 90, 114, 138, 150, + 263, 156, 157, 158, 159, 160, 165, 166, + 167, 168, 256, 260, 152, 151, 153, 18, + 47, 154, 83, 89, 1, 90, 114, 138, + 150, 263, 156, 157, 158, 159, 160, 165, + 166, 167, 168, 260, 264, 18, 47, 83, + 89, 1, 90, 114, 138, 150, 263, 156, + 157, 158, 159, 160, 165, 166, 167, 168, + 264 }; static const char _svg_path_trans_actions[] = { - 15, 0, 0, 0, 0, 9, 47, 47, - 47, 0, 1, 1, 1, 0, 0, 0, - 3, 17, 3, 17, 0, 0, 1, 0, - 1, 1, 0, 0, 0, 60, 20, 56, - 20, 56, 20, 20, 0, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 0, 1, - 0, 1, 1, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 3, - 17, 3, 17, 0, 0, 1, 0, 1, - 1, 0, 0, 0, 68, 23, 64, 23, - 64, 23, 23, 0, 23, 23, 23, 23, - 23, 23, 23, 23, 23, 23, 23, 23, - 23, 23, 23, 23, 23, 9, 47, 47, - 0, 1, 1, 0, 3, 3, 17, 0, - 0, 0, 1, 1, 0, 3, 17, 3, - 17, 0, 0, 1, 0, 1, 1, 0, - 0, 0, 3, 3, 0, 0, 0, 0, - 0, 7, 7, 0, 0, 0, 0, 7, - 7, 0, 1, 0, 1, 1, 0, 0, - 0, 3, 17, 3, 17, 0, 0, 1, - 0, 1, 1, 0, 0, 0, 124, 44, - 44, 120, 44, 44, 0, 44, 44, 44, - 44, 44, 44, 44, 44, 44, 44, 44, - 44, 44, 44, 44, 44, 44, 0, 0, - 0, 0, 9, 47, 47, 47, 0, 1, - 1, 1, 0, 0, 0, 3, 17, 3, - 17, 0, 0, 1, 0, 1, 1, 0, - 0, 0, 3, 17, 3, 17, 0, 0, - 1, 0, 1, 1, 0, 0, 0, 3, - 17, 3, 17, 0, 0, 1, 0, 1, - 1, 0, 0, 0, 3, 17, 3, 17, + 9, 0, 51, 51, 51, 0, 1, 1, + 1, 0, 0, 0, 3, 15, 3, 15, + 0, 0, 1, 0, 1, 1, 0, 0, + 0, 0, 0, 0, 3, 15, 3, 15, 0, 0, 1, 0, 1, 1, 0, 0, - 0, 3, 17, 3, 17, 0, 0, 1, - 0, 1, 1, 0, 0, 0, 92, 32, - 88, 32, 88, 32, 32, 0, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 0, - 0, 0, 9, 47, 47, 47, 0, 1, - 1, 1, 0, 0, 0, 76, 26, 72, - 26, 72, 26, 26, 0, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 0, 0, - 0, 9, 47, 47, 47, 9, 47, 47, - 47, 0, 1, 1, 1, 0, 0, 0, - 3, 17, 3, 17, 0, 0, 1, 0, - 1, 1, 0, 0, 0, 3, 17, 3, - 17, 0, 0, 1, 0, 1, 1, 0, - 0, 0, 3, 17, 3, 17, 0, 0, - 1, 0, 1, 1, 0, 0, 0, 108, - 38, 104, 38, 104, 38, 38, 0, 38, - 38, 38, 38, 38, 38, 38, 38, 38, - 38, 38, 38, 38, 38, 38, 38, 38, - 0, 0, 0, 9, 47, 47, 47, 0, - 1, 1, 1, 0, 0, 0, 3, 17, - 3, 17, 0, 0, 1, 0, 1, 1, - 0, 0, 0, 3, 17, 3, 17, 0, + 0, 0, 1, 1, 1, 9, 51, 51, + 0, 1, 1, 0, 3, 3, 15, 0, + 0, 0, 1, 1, 0, 3, 15, 3, + 15, 0, 0, 1, 0, 1, 1, 0, + 0, 0, 3, 3, 0, 0, 0, 0, + 0, 7, 7, 7, 7, 0, 0, 0, + 0, 7, 48, 7, 48, 48, 0, 1, + 0, 1, 1, 0, 0, 0, 3, 15, + 3, 15, 0, 0, 1, 0, 1, 1, + 0, 0, 0, 0, 0, 0, 9, 51, + 51, 51, 0, 1, 1, 1, 0, 0, + 0, 3, 15, 3, 15, 0, 0, 1, + 0, 1, 1, 0, 0, 0, 3, 15, + 3, 15, 0, 0, 1, 0, 1, 1, + 0, 0, 0, 3, 15, 3, 15, 0, 0, 1, 0, 1, 1, 0, 0, 0, - 3, 17, 3, 17, 0, 0, 1, 0, - 1, 1, 0, 0, 0, 100, 35, 96, - 35, 96, 35, 35, 0, 35, 35, 35, - 35, 35, 35, 35, 35, 35, 35, 35, - 35, 35, 35, 35, 35, 35, 0, 0, - 0, 9, 47, 47, 47, 0, 1, 1, - 1, 0, 0, 0, 3, 17, 3, 17, + 3, 15, 3, 15, 0, 0, 1, 0, + 1, 1, 0, 0, 0, 3, 15, 3, + 15, 0, 0, 1, 0, 1, 1, 0, + 0, 0, 0, 0, 9, 51, 51, 51, + 0, 1, 1, 1, 0, 0, 0, 0, + 0, 9, 51, 51, 51, 9, 51, 51, + 51, 0, 1, 1, 1, 0, 0, 0, + 3, 15, 3, 15, 0, 0, 1, 0, + 1, 1, 0, 0, 0, 3, 15, 3, + 15, 0, 0, 1, 0, 1, 1, 0, + 0, 0, 3, 15, 3, 15, 0, 0, + 1, 0, 1, 1, 0, 0, 0, 0, + 0, 9, 51, 51, 51, 0, 1, 1, + 1, 0, 0, 0, 3, 15, 3, 15, 0, 0, 1, 0, 1, 1, 0, 0, - 0, 116, 41, 112, 41, 112, 41, 41, - 0, 41, 41, 41, 41, 41, 41, 41, - 41, 41, 41, 41, 41, 41, 41, 41, - 41, 41, 0, 0, 0, 9, 47, 47, - 47, 0, 1, 1, 1, 0, 0, 0, - 84, 29, 80, 29, 80, 29, 29, 0, - 29, 29, 29, 29, 29, 29, 29, 29, - 29, 29, 29, 29, 29, 29, 29, 29, - 29, 0, 0, 0, 53, 13, 13, 13, + 0, 3, 15, 3, 15, 0, 0, 1, + 0, 1, 1, 0, 0, 0, 3, 15, + 3, 15, 0, 0, 1, 0, 1, 1, + 0, 0, 0, 0, 0, 9, 51, 51, + 51, 0, 1, 1, 1, 0, 0, 0, + 3, 15, 3, 15, 0, 0, 1, 0, + 1, 1, 0, 0, 0, 0, 0, 9, + 51, 51, 51, 0, 1, 1, 1, 0, + 0, 0, 0, 0, 11, 54, 54, 11, + 54, 54, 54, 11, 54, 54, 54, 11, + 54, 54, 54, 11, 54, 54, 54, 0, + 0, 11, 54, 54, 54, 11, 54, 54, + 54, 11, 54, 54, 54, 11, 54, 54, + 54, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 5, + 45, 5, 45, 45, 5, 5, 5, 5, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 18, 57, 18, 57, + 18, 18, 0, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 21, 61, + 21, 61, 21, 21, 0, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 42, 42, + 89, 42, 42, 0, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 0, 30, 73, + 30, 73, 30, 30, 0, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 0, 24, + 65, 24, 65, 24, 24, 0, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 0, + 36, 81, 36, 81, 36, 36, 0, 36, + 36, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 36, + 0, 33, 77, 33, 77, 33, 33, 0, + 33, 33, 33, 33, 33, 33, 33, 33, + 33, 33, 33, 33, 33, 33, 33, 33, + 33, 0, 39, 85, 39, 85, 39, 39, + 0, 39, 39, 39, 39, 39, 39, 39, + 39, 39, 39, 39, 39, 39, 39, 39, + 39, 39, 0, 27, 69, 27, 69, 27, + 27, 0, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 0, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 0, 11, 50, 50, 11, 50, 50, - 50, 11, 50, 50, 50, 11, 50, 50, - 50, 11, 50, 50, 50, 0, 0, 11, - 50, 50, 50, 11, 50, 50, 50, 11, - 50, 50, 50, 11, 50, 50, 50, 0, + 0 +}; + +static const char _svg_path_eof_actions[] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 5, 5, 5, - 5, 0, 0, 0, 0, 0, 0, 0, - 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 18, 0, 21, 21, 42, 0, 30, + 0, 30, 30, 24, 0, 24, 24, 36, + 0, 36, 36, 33, 0, 33, 33, 39, + 0, 39, 39, 27, 0, 27, 27, 13, + 0, 18, 18, 42, 42, 21 }; -static const int svg_path_start = 1; -static const int svg_path_first_final = 270; +static const int svg_path_start = 232; +static const int svg_path_first_final = 232; -//static const int svg_path_en_main = 1; +static const int svg_path_en_main = 232; -void Parser::parse(char const *str) -throw(SVGPathParseError) +#line 47 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl" + + +SVGPathParser::SVGPathParser(PathSink &sink) + : _absolute(false) + , _sink(sink) + , _z_snap_threshold(0) + , _curve(NULL) { - char const *p = str; - char const *start = NULL; - int cs; + reset(); +} + +SVGPathParser::~SVGPathParser() +{ + delete _curve; +} - _reset(); +void SVGPathParser::reset() { + _absolute = false; + _current = _initial = Point(0, 0); + _quad_tangent = _cubic_tangent = Point(0, 0); + _params.clear(); + delete _curve; + _curve = NULL; + +#line 1105 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.cpp" { cs = svg_path_start; } +#line 73 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl" + +} + +void SVGPathParser::parse(char const *str, int len) +{ + if (len < 0) { + len = std::strlen(str); + } + _parse(str, str + len, true); +} + +void SVGPathParser::parse(std::string const &s) +{ + _parse(s.c_str(), s.c_str() + s.size(), true); +} + +void SVGPathParser::feed(char const *str, int len) +{ + if (len < 0) { + len = std::strlen(str); + } + _parse(str, str + len, false); +} + +void SVGPathParser::feed(std::string const &s) +{ + _parse(s.c_str(), s.c_str() + s.size(), false); +} + +void SVGPathParser::finish() +{ + char const *empty = ""; + _parse(empty, empty, true); +} + +void SVGPathParser::_push(Coord value) +{ + _params.push_back(value); +} + +Coord SVGPathParser::_pop() +{ + Coord value = _params.back(); + _params.pop_back(); + return value; +} + +bool SVGPathParser::_pop_flag() +{ + return _pop() != 0.0; +} + +Coord SVGPathParser::_pop_coord(Dim2 axis) +{ + if (_absolute) { + return _pop(); + } else { + return _pop() + _current[axis]; + } +} + +Point SVGPathParser::_pop_point() +{ + Coord y = _pop_coord(Y); + Coord x = _pop_coord(X); + return Point(x, y); +} + +void SVGPathParser::_moveTo(Point const &p) +{ + _pushCurve(NULL); // flush + _sink.moveTo(p); + _quad_tangent = _cubic_tangent = _current = _initial = p; +} + +void SVGPathParser::_lineTo(Point const &p) +{ + _pushCurve(new LineSegment(_current, p)); + _quad_tangent = _cubic_tangent = _current = p; +} + +void SVGPathParser::_curveTo(Point const &c0, Point const &c1, Point const &p) +{ + _pushCurve(new CubicBezier(_current, c0, c1, p)); + _quad_tangent = _current = p; + _cubic_tangent = p + ( p - c1 ); +} + +void SVGPathParser::_quadTo(Point const &c, Point const &p) +{ + _pushCurve(new QuadraticBezier(_current, c, p)); + _cubic_tangent = _current = p; + _quad_tangent = p + ( p - c ); +} + +void SVGPathParser::_arcTo(Coord rx, Coord ry, Coord angle, + bool large_arc, bool sweep, Point const &p) +{ + if (_current == p) { + return; // ignore invalid (ambiguous) arc segments where start and end point are the same (per SVG spec) + } + + _pushCurve(new EllipticalArc(_current, rx, ry, angle, large_arc, sweep, p)); + _quad_tangent = _cubic_tangent = _current = p; +} + +void SVGPathParser::_closePath() +{ + if (_curve && (!_absolute || !_moveto_was_absolute) && + are_near(_initial, _current, _z_snap_threshold)) + { + _curve->setFinal(_initial); + } + + _pushCurve(NULL); // flush + _sink.closePath(); + _quad_tangent = _cubic_tangent = _current = _initial; +} + +void SVGPathParser::_pushCurve(Curve *c) +{ + if (_curve) { + _sink.feed(*_curve, false); + delete _curve; + } + _curve = c; +} +void SVGPathParser::_parse(char const *str, char const *strend, bool finish) +{ + char const *p = str; + char const *pe = strend; + char const *eof = finish ? pe : NULL; + char const *start = NULL; + + +#line 1247 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.cpp" { int _klen; unsigned int _trans; @@ -1168,6 +1251,8 @@ throw(SVGPathParseError) unsigned int _nacts; const char *_keys; + if ( p == pe ) + goto _test_eof; if ( cs == 0 ) goto _out; _resume: @@ -1233,63 +1318,76 @@ _match: switch ( *_acts++ ) { case 0: +#line 209 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl" { start = p; } break; case 1: - +#line 213 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl" { - char const *end=p; - std::string buf(start, end); - _push(g_ascii_strtod(buf.c_str(), NULL)); - start = NULL; + if (start) { + std::string buf(start, p); + _push(g_ascii_strtod(buf.c_str(), NULL)); + start = NULL; + } else { + std::string buf(str, p); + _push(g_ascii_strtod((_number_part + buf).c_str(), NULL)); + _number_part.clear(); + } } break; case 2: - +#line 225 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl" { _push(1.0); } break; case 3: - +#line 229 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl" { _push(0.0); } break; case 4: - +#line 233 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl" { _absolute = true; } break; case 5: +#line 237 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl" { _absolute = false; } break; case 6: +#line 241 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl" { + _moveto_was_absolute = _absolute; _moveTo(_pop_point()); } break; case 7: +#line 246 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl" { _lineTo(_pop_point()); } break; case 8: +#line 250 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl" { - _hlineTo(Point(_pop_coord(X), _current[Y])); + _lineTo(Point(_pop_coord(X), _current[Y])); } break; case 9: +#line 254 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl" { - _vlineTo(Point(_current[X], _pop_coord(Y))); + _lineTo(Point(_current[X], _pop_coord(Y))); } break; case 10: +#line 258 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl" { Point p = _pop_point(); Point c1 = _pop_point(); @@ -1298,6 +1396,7 @@ _match: } break; case 11: +#line 265 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl" { Point p = _pop_point(); Point c1 = _pop_point(); @@ -1305,6 +1404,7 @@ _match: } break; case 12: +#line 271 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl" { Point p = _pop_point(); Point c = _pop_point(); @@ -1312,12 +1412,14 @@ _match: } break; case 13: +#line 277 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl" { Point p = _pop_point(); _quadTo(_quad_tangent, p); } break; case 14: +#line 282 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl" { Point point = _pop_point(); bool sweep = _pop_flag(); @@ -1330,42 +1432,169 @@ _match: } break; case 15: +#line 293 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl" { _closePath(); } break; - case 16: - {{p++; goto _out; }} - break; +#line 1441 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.cpp" } } _again: if ( cs == 0 ) goto _out; - p += 1; - goto _resume; + if ( ++p != pe ) + goto _resume; + _test_eof: {} + if ( p == eof ) + { + const char *__acts = _svg_path_actions + _svg_path_eof_actions[cs]; + unsigned int __nacts = (unsigned int) *__acts++; + while ( __nacts-- > 0 ) { + switch ( *__acts++ ) { + case 1: +#line 213 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl" + { + if (start) { + std::string buf(start, p); + _push(g_ascii_strtod(buf.c_str(), NULL)); + start = NULL; + } else { + std::string buf(str, p); + _push(g_ascii_strtod((_number_part + buf).c_str(), NULL)); + _number_part.clear(); + } + } + break; + case 6: +#line 241 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl" + { + _moveto_was_absolute = _absolute; + _moveTo(_pop_point()); + } + break; + case 7: +#line 246 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl" + { + _lineTo(_pop_point()); + } + break; + case 8: +#line 250 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl" + { + _lineTo(Point(_pop_coord(X), _current[Y])); + } + break; + case 9: +#line 254 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl" + { + _lineTo(Point(_current[X], _pop_coord(Y))); + } + break; + case 10: +#line 258 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl" + { + Point p = _pop_point(); + Point c1 = _pop_point(); + Point c0 = _pop_point(); + _curveTo(c0, c1, p); + } + break; + case 11: +#line 265 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl" + { + Point p = _pop_point(); + Point c1 = _pop_point(); + _curveTo(_cubic_tangent, c1, p); + } + break; + case 12: +#line 271 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl" + { + Point p = _pop_point(); + Point c = _pop_point(); + _quadTo(c, p); + } + break; + case 13: +#line 277 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl" + { + Point p = _pop_point(); + _quadTo(_quad_tangent, p); + } + break; + case 14: +#line 282 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl" + { + Point point = _pop_point(); + bool sweep = _pop_flag(); + bool large_arc = _pop_flag(); + double angle = deg_to_rad(_pop()); + double ry = _pop(); + double rx = _pop(); + + _arcTo(rx, ry, angle, large_arc, sweep, point); + } + break; + case 15: +#line 293 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl" + { + _closePath(); + } + break; +#line 1547 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.cpp" + } + } + } + _out: {} } +#line 435 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl" - if ( cs < svg_path_first_final ) { - throw SVGPathParseError(); + + if (finish) { + if (cs < svg_path_first_final) { + throw SVGPathParseError(); + } + } else if (start != NULL) { + _number_part = std::string(start, pe); } -} + if (finish) { + _pushCurve(NULL); + _sink.flush(); + reset(); + } } void parse_svg_path(char const *str, PathSink &sink) -throw(SVGPathParseError) { - Parser parser(sink); + SVGPathParser parser(sink); parser.parse(str); - sink.flush(); } +void parse_svg_path_file(FILE *fi, PathSink &sink) +{ + static const size_t BUFFER_SIZE = 4096; + char buffer[BUFFER_SIZE]; + size_t bytes_read; + SVGPathParser parser(sink); + + while (true) { + bytes_read = fread(buffer, 1, BUFFER_SIZE, fi); + if (bytes_read < BUFFER_SIZE) { + parser.parse(buffer, bytes_read); + break; + } else { + parser.feed(buffer, bytes_read); + } + } } +} // namespace Geom + /* Local Variables: mode:c++ diff --git a/src/2geom/svg-path-parser.h b/src/2geom/svg-path-parser.h index 163fbe5c4..e25316cf8 100644 --- a/src/2geom/svg-path-parser.h +++ b/src/2geom/svg-path-parser.h @@ -30,48 +30,161 @@ * */ -#ifndef SEEN_SVG_PATH_PARSER_H -#define SEEN_SVG_PATH_PARSER_H +#ifndef LIB2GEOM_SEEN_SVG_PATH_PARSER_H +#define LIB2GEOM_SEEN_SVG_PATH_PARSER_H -#include <vector> +#include <iostream> #include <iterator> #include <stdexcept> +#include <vector> +#include <cstdio> #include <2geom/exception.h> #include <2geom/point.h> #include <2geom/path-sink.h> +#include <2geom/forward.h> namespace Geom { -void parse_svg_path(char const *str, PathSink &sink) throw(SVGPathParseError); +/** @brief Read SVG path data and feed it to a PathSink + * + * This class provides an interface to an SVG path data parser written in Ragel. + * It supports parsing the path data either at once or block-by-block. + * Use the parse() functions to parse complete data and the feed() and finish() + * functions to parse partial data. + * + * The parser will call the appropriate methods on the PathSink supplied + * at construction. To store the path in memory as a PathVector, pass + * a PathBuilder. You can also use one of the freestanding helper functions + * if you don't need to parse data block-by-block. + * + * @ingroup Paths + */ +class SVGPathParser { +public: + SVGPathParser(PathSink &sink); + ~SVGPathParser(); + + /** @brief Reset internal state. + * Discards the internal state associated with partially parsed data, + * letting you start from scratch. Note that any partial data written + * to the path sink is not affected - you need to clear it yourself. */ + void reset(); + + /** @brief Parse a C-style string. + * The path sink is flushed and the internal state is reset after this call. + * Note that the state is not reset before this method, so you can use it to + * process the last block of partial data. + * @param str String to parse + * @param len Length of string or -1 if null-terminated */ + void parse(char const *str, int len = -1); + /** @brief Parse an STL string. */ + void parse(std::string const &s); + + /** @brief Parse a part of path data stored in a C-style string. + * This method does not reset internal state, so it can be called multiple + * times to parse successive blocks of a longer SVG path data string. + * To finish parsing, call finish() after the final block or call parse() + * with the last block of data. + * @param str String to parse + * @param len Length of string or -1 if null-terminated */ + void feed(char const *str, int len = -1); + /** @brief Parse a part of path data stored in an STL string. */ + void feed(std::string const &s); + + /** @brief Finalize parsing. + * After the last block of data was submitted with feed(), call this method + * to finalize parsing, flush the path sink and reset internal state. + * You should not call this after parse(). */ + void finish(); -inline std::vector<Path> parse_svg_path(char const *str) throw(SVGPathParseError) { - typedef std::vector<Path> Subpaths; - typedef std::back_insert_iterator<Subpaths> Inserter; - - Subpaths subpaths; - Inserter iter(subpaths); - PathIteratorSink<Inserter> generator(iter); + /** @brief Set the threshold for considering the closing segment degenerate. + * When the current point was reached by a relative command, is closer + * to the initial point of the path than the specified threshold + * and a 'z' is encountered, the last segment will be adjusted instead so that + * the closing segment has exactly zero length. This is useful when reading + * SVG 1.1 paths that have non-linear final segments written in relative + * coordinates, which always suffer from some loss of precision. SVG 2 + * allows alternate placement of 'z' which does not have this problem. */ + void setZSnapThreshold(Coord threshold) { _z_snap_threshold = threshold; } + Coord zSnapThreshold() const { return _z_snap_threshold; } + +private: + bool _absolute; + bool _moveto_was_absolute; + Point _current; + Point _initial; + Point _cubic_tangent; + Point _quad_tangent; + std::vector<Coord> _params; + PathSink &_sink; + Coord _z_snap_threshold; + Curve *_curve; + + int cs; + std::string _number_part; + + void _push(Coord value); + Coord _pop(); + bool _pop_flag(); + Coord _pop_coord(Geom::Dim2 axis); + Point _pop_point(); + void _moveTo(Point const &p); + void _lineTo(Point const &p); + void _curveTo(Point const &c0, Point const &c1, Point const &p); + void _quadTo(Point const &c, Point const &p); + void _arcTo(double rx, double ry, double angle, + bool large_arc, bool sweep, Point const &p); + void _closePath(); + void _pushCurve(Curve *c); + + void _parse(char const *str, char const *strend, bool finish); +}; + +/** @brief Feed SVG path data to the specified sink + * @ingroup Paths */ +void parse_svg_path(char const *str, PathSink &sink); +/** @brief Feed SVG path data to the specified sink + * @ingroup Paths */ +inline void parse_svg_path(std::string const &str, PathSink &sink) { + parse_svg_path(str.c_str(), sink); +} +/** Feed SVG path data from a C stream to the specified sink + * @ingroup Paths */ +void parse_svg_path_file(FILE *fi, PathSink &sink); + +/** @brief Create path vector from SVG path data stored in a C string + * @ingroup Paths */ +inline PathVector parse_svg_path(char const *str) { + PathVector ret; + SubpathInserter iter(ret); + PathIteratorSink<SubpathInserter> generator(iter); parse_svg_path(str, generator); - return subpaths; + return ret; } -inline std::vector<Path> read_svgd_f(FILE * fi) throw(SVGPathParseError) { - /// @bug The 10kB length limit should be removed - char input[1024 * 10]; - fgets(input, 1024 * 10, fi); - return parse_svg_path(input); +/** @brief Create path vector from a C stream with SVG path data + * @ingroup Paths */ +inline PathVector read_svgd_f(FILE * fi) { + PathVector ret; + SubpathInserter iter(ret); + PathIteratorSink<SubpathInserter> generator(iter); + + parse_svg_path_file(fi, generator); + return ret; } -inline std::vector<Path> read_svgd(char const * name) throw(SVGPathParseError) { - FILE* fi = fopen(name, "r"); +/** @brief Create path vector from SVG path data stored in a file + * @ingroup Paths */ +inline PathVector read_svgd(char const *filename) { + FILE* fi = fopen(filename, "r"); if(fi == NULL) throw(std::runtime_error("Error opening file")); - std::vector<Path> out = read_svgd_f(fi); + PathVector out = read_svgd_f(fi); fclose(fi); return out; } -} +} // end namespace Geom #endif /* diff --git a/src/2geom/svg-path-writer.cpp b/src/2geom/svg-path-writer.cpp new file mode 100644 index 000000000..1c40ba64e --- /dev/null +++ b/src/2geom/svg-path-writer.cpp @@ -0,0 +1,296 @@ +/** @file + * @brief Path sink which writes an SVG-compatible command string + *//* + * Authors: + * Krzysztof Kosiński <tweenk.pl@gmail.com> + * + * Copyright 2014 Authors + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + */ + +#include <cmath> +#include <iomanip> +#include <2geom/coord.h> +#include <2geom/svg-path-writer.h> +#include <glib.h> + +namespace Geom { + +static inline bool is_digit(char c) { + return c >= '0' && c <= '9'; +} + +SVGPathWriter::SVGPathWriter() + : _epsilon(0) + , _precision(-1) + , _optimize(false) + , _use_shorthands(true) + , _command(0) +{ + // always use C locale for number formatting + _ns.imbue(std::locale::classic()); + _ns.unsetf(std::ios::floatfield); +} + +void SVGPathWriter::moveTo(Point const &p) +{ + _setCommand('M'); + _current_pars.push_back(p[X]); + _current_pars.push_back(p[Y]); + + _current = _subpath_start = _quad_tangent = _cubic_tangent = p; + if (!_optimize) { + flush(); + } +} + +void SVGPathWriter::lineTo(Point const &p) +{ + // The weird setting of _current is to avoid drift with many almost-aligned segments + // The additional conditions ensure that the smaller dimension is rounded to zero + bool written = false; + if (_use_shorthands) { + Point r = _current - p; + if (are_near(p[X], _current[X], _epsilon) && std::abs(r[X]) < std::abs(r[Y])) { + // emit vlineto + _setCommand('V'); + _current_pars.push_back(p[Y]); + _current[Y] = p[Y]; + written = true; + } else if (are_near(p[Y], _current[Y], _epsilon) && std::abs(r[Y]) < std::abs(r[X])) { + // emit hlineto + _setCommand('H'); + _current_pars.push_back(p[X]); + _current[X] = p[X]; + written = true; + } + } + + if (!written) { + // emit normal lineto + if (_command != 'M' && _command != 'L') { + _setCommand('L'); + } + _current_pars.push_back(p[X]); + _current_pars.push_back(p[Y]); + _current = p; + } + + _cubic_tangent = _quad_tangent = _current; + if (!_optimize) { + flush(); + } +} + +void SVGPathWriter::quadTo(Point const &c, Point const &p) +{ + bool shorthand = _use_shorthands && are_near(c, _quad_tangent, _epsilon); + + _setCommand(shorthand ? 'T' : 'Q'); + if (!shorthand) { + _current_pars.push_back(c[X]); + _current_pars.push_back(c[Y]); + } + _current_pars.push_back(p[X]); + _current_pars.push_back(p[Y]); + + _current = _cubic_tangent = p; + _quad_tangent = p + (p - c); + if (!_optimize) { + flush(); + } +} + +void SVGPathWriter::curveTo(Point const &p1, Point const &p2, Point const &p3) +{ + bool shorthand = _use_shorthands && are_near(p1, _cubic_tangent, _epsilon); + + _setCommand(shorthand ? 'S' : 'C'); + if (!shorthand) { + _current_pars.push_back(p1[X]); + _current_pars.push_back(p1[Y]); + } + _current_pars.push_back(p2[X]); + _current_pars.push_back(p2[Y]); + _current_pars.push_back(p3[X]); + _current_pars.push_back(p3[Y]); + + _current = _quad_tangent = p3; + _cubic_tangent = p3 + (p3 - p2); + if (!_optimize) { + flush(); + } +} + +void SVGPathWriter::arcTo(double rx, double ry, double angle, + bool large_arc, bool sweep, Point const &p) +{ + _setCommand('A'); + _current_pars.push_back(rx); + _current_pars.push_back(ry); + _current_pars.push_back(rad_to_deg(angle)); + _current_pars.push_back(large_arc ? 1. : 0.); + _current_pars.push_back(sweep ? 1. : 0.); + _current_pars.push_back(p[X]); + _current_pars.push_back(p[Y]); + + _current = _quad_tangent = _cubic_tangent = p; + if (!_optimize) { + flush(); + } +} + +void SVGPathWriter::closePath() +{ + flush(); + if (_optimize) { + _s << "z"; + } else { + _s << " z"; + } + _current = _quad_tangent = _cubic_tangent = _subpath_start; +} + +void SVGPathWriter::flush() +{ + if (_command == 0 || _current_pars.empty()) return; + + if (_optimize) { + _s << _command; + } else { + if (_s.tellp() != 0) { + _s << ' '; + } + _s << _command; + } + + char lastchar = _command; + bool contained_dot = false; + + for (unsigned i = 0; i < _current_pars.size(); ++i) { + // TODO: optimize the use of absolute / relative coords + std::string cs = _formatCoord(_current_pars[i]); + + // Separator handling logic. + // Floating point values can end with a digit or dot + // and start with a digit, a plus or minus sign, or a dot. + // The following cases require a separator: + // * digit-digit + // * digit-dot (only if the previous number didn't contain a dot) + // * dot-digit + if (_optimize) { + // C++11: change to front() + char firstchar = cs[0]; + if (is_digit(lastchar)) { + if (is_digit(firstchar)) { + _s << " "; + } else if (firstchar == '.' && !contained_dot) { + _s << " "; + } + } else if (lastchar == '.' && is_digit(firstchar)) { + _s << " "; + } + _s << cs; + + // C++11: change to back() + lastchar = cs[cs.length()-1]; + contained_dot = cs.find('.') != std::string::npos; + } else { + _s << " " << cs; + } + } + _current_pars.clear(); + _command = 0; +} + +void SVGPathWriter::clear() +{ + _s.clear(); + _s.str(""); + _ns.clear(); + _ns.str(""); + _command = 0; + _current_pars.clear(); + _current = Point(0,0); + _subpath_start = Point(0,0); +} + +void SVGPathWriter::setPrecision(int prec) +{ + _precision = prec; + if (prec < 0) { + _epsilon = 0; + } else { + _epsilon = std::pow(10., -prec); + _ns << std::setprecision(_precision); + } +} + +void SVGPathWriter::_setCommand(char cmd) +{ + if (_command != 0 && _command != cmd) { + flush(); + } + _command = cmd; +} + +std::string SVGPathWriter::_formatCoord(Coord par) +{ + std::string ret; + if (_precision < 0) { + ret = format_coord_shortest(par); + } else { + _ns << par; + ret = _ns.str(); + _ns.clear(); + _ns.str(""); + } + return ret; +} + + +std::string write_svg_path(PathVector const &pv, int prec, bool optimize, bool shorthands) +{ + SVGPathWriter writer; + writer.setPrecision(prec); + writer.setOptimize(optimize); + writer.setUseShorthands(shorthands); + + writer.feed(pv); + return writer.str(); +} + +} // namespace Geom + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/2geom/svg-path-writer.h b/src/2geom/svg-path-writer.h new file mode 100644 index 000000000..e639541ab --- /dev/null +++ b/src/2geom/svg-path-writer.h @@ -0,0 +1,122 @@ +/** @file + * @brief Path sink which writes an SVG-compatible command string + *//* + * Authors: + * Krzysztof Kosiński <tweenk.pl@gmail.com> + * + * Copyright 2014 Authors + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + */ + +#ifndef LIB2GEOM_SEEN_SVG_PATH_WRITER_H +#define LIB2GEOM_SEEN_SVG_PATH_WRITER_H + +#include <2geom/path-sink.h> +#include <sstream> + +namespace Geom { + +/** @brief Serialize paths to SVG path data strings. + * You can access the generated string by calling the str() method. + * @ingroup Paths + */ +class SVGPathWriter + : public PathSink +{ +public: + SVGPathWriter(); + ~SVGPathWriter() {} + + void moveTo(Point const &p); + void lineTo(Point const &p); + void quadTo(Point const &c, Point const &p); + void curveTo(Point const &c0, Point const &c1, Point const &p); + void arcTo(double rx, double ry, double angle, + bool large_arc, bool sweep, Point const &p); + void closePath(); + void flush(); + + /// Clear any path data written so far. + void clear(); + + /** @brief Set output precision. + * When the parameter is negative, the path writer enters a verbatim mode + * which preserves all values exactly. */ + void setPrecision(int prec); + + /** @brief Enable or disable length optimization. + * + * When set to true, the path writer will optimize the generated path data + * for minimum length. However, this will make the data less readable, + * because spaces between commands and coordinates will be omitted where + * unnecessary for correct parsing. + * + * When set to false, the string will be a straightforward, partially redundant + * representation of the passed commands, optimized for readability. + * Commands and coordinates will always be separated by spaces and the command + * symbol will not be omitted for multiple consecutive commands of the same type. + * + * Length optimization is turned off by default. */ + void setOptimize(bool opt) { _optimize = opt; } + + /** @brief Enable or disable the use of V, H, T and S commands where possible. + * Shorthands are turned on by default. */ + void setUseShorthands(bool use) { _use_shorthands = use; } + + /// Retrieve the generated path data string. + std::string str() const { return _s.str(); } + +private: + void _setCommand(char cmd); + std::string _formatCoord(Coord par); + + std::ostringstream _s, _ns; + std::vector<Coord> _current_pars; + Point _subpath_start; + Point _current; + Point _quad_tangent; + Point _cubic_tangent; + Coord _epsilon; + int _precision; + bool _optimize; + bool _use_shorthands; + char _command; +}; + +std::string write_svg_path(PathVector const &pv, int prec = -1, bool optimize = false, bool shorthands = true); + +} // namespace Geom + +#endif // LIB2GEOM_SEEN_SVG_PATH_WRITER_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:encoding=utf-8:textwidth=99 : diff --git a/src/2geom/sweep.cpp b/src/2geom/sweep-bounds.cpp index f25894282..48f168b97 100644 --- a/src/2geom/sweep.cpp +++ b/src/2geom/sweep-bounds.cpp @@ -1,9 +1,27 @@ -#include <2geom/sweep.h> +#include <2geom/sweep-bounds.h> #include <algorithm> namespace Geom { +struct Event { + double x; + unsigned ix; + bool closing; + Event(double pos, unsigned i, bool c) : x(pos), ix(i), closing(c) {} +// Lexicographic ordering by x then closing + bool operator<(Event const &other) const { + if(x < other.x) return true; + if(x > other.x) return false; + return closing < other.closing; + } + bool operator==(Event const &other) const { + return other.x == x && other.ix == ix && other.closing == closing; + } +}; + +std::vector<std::vector<unsigned> > fake_cull(unsigned a, unsigned b); + /** * \brief Make a list of pairs of self intersections in a list of Rects. * @@ -19,8 +37,8 @@ std::vector<std::vector<unsigned> > sweep_bounds(std::vector<Rect> rs, Dim2 d) { std::vector<std::vector<unsigned> > pairs(rs.size()); for(unsigned i = 0; i < rs.size(); i++) { - events.push_back(Event(rs[i][d][0], i, false)); - events.push_back(Event(rs[i][d][1], i, true)); + events.push_back(Event(rs[i][d].min(), i, false)); + events.push_back(Event(rs[i][d].max(), i, true)); } std::sort(events.begin(), events.end()); @@ -66,8 +84,8 @@ std::vector<std::vector<unsigned> > sweep_bounds(std::vector<Rect> a, std::vecto events[n].reserve(sz*2); for(unsigned i = 0; i < sz; i++) { Rect r = n ? b[i] : a[i]; - events[n].push_back(Event(r[d][0], i, false)); - events[n].push_back(Event(r[d][1], i, true)); + events[n].push_back(Event(r[d].min(), i, false)); + events[n].push_back(Event(r[d].max(), i, true)); } std::sort(events[n].begin(), events[n].end()); } diff --git a/src/2geom/sweep.h b/src/2geom/sweep-bounds.h index 91371e6fb..e0ebf2975 100644 --- a/src/2geom/sweep.h +++ b/src/2geom/sweep-bounds.h @@ -32,37 +32,20 @@ * */ -#ifndef __2GEOM_SWEEP_H__ -#define __2GEOM_SWEEP_H__ +#ifndef LIB2GEOM_SEEN_SWEEP_H +#define LIB2GEOM_SEEN_SWEEP_H #include <vector> #include <2geom/d2.h> namespace Geom { -struct Event { - double x; - unsigned ix; - bool closing; - Event(double pos, unsigned i, bool c) : x(pos), ix(i), closing(c) {} -// Lexicographic ordering by x then closing - bool operator<(Event const &other) const { - if(x < other.x) return true; - if(x > other.x) return false; - return closing < other.closing; - } - bool operator==(Event const &other) const { - return other.x == x && other.ix == ix && other.closing == closing; - } -}; std::vector<std::vector<unsigned> > sweep_bounds(std::vector<Rect>, Dim2 dim = X); std::vector<std::vector<unsigned> > sweep_bounds(std::vector<Rect>, std::vector<Rect>, Dim2 dim = X); -std::vector<std::vector<unsigned> > fake_cull(unsigned a, unsigned b); - } #endif diff --git a/src/2geom/sweeper.h b/src/2geom/sweeper.h new file mode 100644 index 000000000..8c4e182a6 --- /dev/null +++ b/src/2geom/sweeper.h @@ -0,0 +1,252 @@ +/** @file + * @brief Class for implementing sweepline algorithms + *//* + * Authors: + * Krzysztof Kosiński <tweenk.pl@gmail.com> + * + * Copyright 2015 Authors + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + */ + +#ifndef LIB2GEOM_SEEN_SWEEPER_H +#define LIB2GEOM_SEEN_SWEEPER_H + +#include <2geom/coord.h> +#include <algorithm> +#include <vector> +#include <boost/intrusive/list.hpp> +#include <boost/range/algorithm/heap_algorithm.hpp> + +namespace Geom { + +/** @brief Sweep traits class for interval bounds. + * @relates Sweeper + * @ingroup Utilities */ +struct IntervalSweepTraits { + typedef Interval Bound; + typedef std::less<Coord> Compare; + inline static Coord entry_value(Bound const &b) { return b.min(); } + inline static Coord exit_value(Bound const &b) { return b.max(); } +}; + +/** @brief Sweep traits class for rectangle bounds. + * @tparam D Which axis to use for sweeping + * @ingroup Utilities */ +template <Dim2 D> +struct RectSweepTraits { + typedef Rect Bound; + typedef std::less<Coord> Compare; + inline static Coord entry_value(Bound const &b) { return b[D].min(); } + inline static Coord exit_value(Bound const &b) { return b[D].max(); } +}; + +template <typename T> +struct BoundsFast { + Rect operator()(T const &item) const { + return item.boundsFast(); + } +}; + +/** @brief Generic sweepline algorithm. + * + * This class encapsulates an algorithm that sorts the objects according + * to their bounds, then moves an imaginary line (sweepline) over those + * bounds from left to right. Objects are added to the active list when + * the line starts intersecting their bounds, and removed when it completely + * passes over them. + * + * To use this, create a derived class and reimplement the _enter() + * and/or _leave() virtual functions, insert all the objects, + * and finally call process(). Inside _enter() and _leave(), the items that have + * their bounds intersected by the sweepline are available in a list called + * _active_items. This is an intrusive linked list, so you should access it using + * iterators. Do not add or remove items from it. You can specify the bound type + * and how it should be accessed by defining a custom SweepTraits class. + * + * Look in path.cpp for example usage. + * + * @tparam Item The type of items to sweep + * @tparam SweepTraits Traits class that defines the items' bounds, + * how to interpret them and how to sort the events + * @ingroup Utilities + */ +template <typename Item, typename SweepTraits = IntervalSweepTraits> +class Sweeper { +public: + /// Type of the item's boundary - usually this will be an Interval or Rect. + typedef typename SweepTraits::Bound Bound; + + Sweeper() {} + + /** @brief Insert a single item for sweeping. + * @param bound Boundary of the item, as defined in sweep traits + * @param item The item itself */ + void insert(Bound const &bound, Item const &item) { + assert(!(typename SweepTraits::Compare()( + SweepTraits::exit_value(bound), + SweepTraits::entry_value(bound)))); + _items.push_back(Record(bound, item)); + } + + /** @brief Insert a range of items using the supplied bounds functor. + * The bounds are computed from items using the supplied bounds functor. + * @param first Start of range + * @param last End of range (one-past-the-end iterator) + * @param f Bounds functor */ + template <typename Iter, typename BoundFunc> + void insert(Iter first, Iter last, BoundFunc f = BoundFunc()) { + for (; first != last; ++first) { + Bound b = f(*first); + assert(!(typename SweepTraits::Compare()( + SweepTraits::exit_value(b), + SweepTraits::entry_value(b)))); + _items.push_back(Record(b, *first)); + } + } + + /** @brief Process entry and exit events. + * This will iterate over all inserted items, calling the virtual protected + * functions _enter() and _leave() according to the order of the boundaries + * of each item. */ + void process() { + if (_items.empty()) return; + + typename SweepTraits::Compare cmp; + + // we store the events in heaps, which is slightly more efficient + // than sorting them, since a heap requires linear time to construct + for (RecordIter i = _items.begin(); i != _items.end(); ++i) { + _entry_events.push_back(i); + _exit_events.push_back(i); + } + boost::make_heap(_entry_events, _entry_heap_compare); + boost::make_heap(_exit_events, _exit_heap_compare); + boost::pop_heap(_entry_events, _entry_heap_compare); + boost::pop_heap(_exit_events, _exit_heap_compare); + + RecordIter next_entry = _entry_events.back(); + RecordIter next_exit = _exit_events.back(); + _entry_events.pop_back(); + _exit_events.pop_back(); + + while (next_entry != _items.end() || next_exit != _items.end()) { + assert(next_exit != _items.end()); + + if (next_entry == _items.end() || + cmp(SweepTraits::exit_value(next_exit->bound), + SweepTraits::entry_value(next_entry->bound))) + { + // exit event - remove record from active list + _leave(*next_exit); + _active_items.erase(_active_items.iterator_to(*next_exit)); + if (!_exit_events.empty()) { + boost::pop_heap(_exit_events, _exit_heap_compare); + next_exit = _exit_events.back(); + _exit_events.pop_back(); + } else { + next_exit = _items.end(); + // we should end the loop after this happens + } + } else { + // entry event - add record to active list + _enter(*next_entry); + _active_items.push_back(*next_entry); + if (!_entry_events.empty()) { + boost::pop_heap(_entry_events, _entry_heap_compare); + next_entry = _entry_events.back(); + _entry_events.pop_back(); + } else { + next_entry = _items.end(); + } + } + } + + assert(_active_items.empty()); + } + +protected: + /// The item and its sweepline boundary. + struct Record { + boost::intrusive::list_member_hook<> _hook; + Bound bound; + Item item; + + Record(Bound const &b, Item const &i) + : bound(b), item(i) + {} + }; + typedef typename std::vector<Record>::iterator RecordIter; + + typedef boost::intrusive::list + < Record + , boost::intrusive::member_hook + < Record + , boost::intrusive::list_member_hook<> + , &Record::_hook + > + > RecordList; + + /** @brief Enter an item record. + * Override this to process an item as it is about to enter the active list. + * When called, the passed record will not be part of the active list. */ + virtual void _enter(Record const &) {} + /** @brief Leave an item record. + * Override this to process an item as it is about to leave the active list. + * When called, the passed record will be part of the active list. */ + virtual void _leave(Record const &) {} + + /// The list of all item records undergoing sweeping. + std::vector<Record> _items; + /// The list of active item records. + RecordList _active_items; + +private: + inline static bool _entry_heap_compare(RecordIter a, RecordIter b) { + typename SweepTraits::Compare cmp; + return cmp(SweepTraits::entry_value(b->bound), SweepTraits::entry_value(a->bound)); + } + inline static bool _exit_heap_compare(RecordIter a, RecordIter b) { + typename SweepTraits::Compare cmp; + return cmp(SweepTraits::exit_value(b->bound), SweepTraits::exit_value(a->bound)); + } + + std::vector<RecordIter> _entry_events; + std::vector<RecordIter> _exit_events; +}; + +} // namespace Geom + +#endif // !LIB2GEOM_SEEN_SWEEPER_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/2geom/sync.sh b/src/2geom/sync.sh new file mode 100755 index 000000000..a2c162903 --- /dev/null +++ b/src/2geom/sync.sh @@ -0,0 +1,31 @@ +#!/bin/bash +set -e + +function usage { + echo "2Geom sync to upstream script" + echo "Usage: $0 path/to/2geom/checkout/dir" +} + +if [ "x$(which rsync)" = "x" ]; then + echo "rsync not found on your system, please install it" + exit 1 +fi + +if [ "x$1" = "x" ]; then + usage $0 + exit 64 +fi +if [ ! -d "$1" ]; then + usage $0 + exit 64 +fi +if [ ! -f "$1/src/2geom/path.h" ]; then + usage $0 + exit 64 +fi + +INK_2GEOM="$(dirname $0)/" +UPSTREAM_2GEOM="$1/src/2geom/" +rsync -r --existing \ + --exclude CMakeLists.txt --exclude sync.sh \ + "$UPSTREAM_2GEOM" "$INK_2GEOM" diff --git a/src/2geom/toposweep.cpp b/src/2geom/toposweep.cpp index 4da3f6922..3cb231142 100644 --- a/src/2geom/toposweep.cpp +++ b/src/2geom/toposweep.cpp @@ -189,7 +189,7 @@ bool SectionSorter::operator()(Section const &a, Section const &b) const { } } - return Point::LexOrderRt(dim)(a.fp, b.fp); + return Point::LexLessRt(dim)(a.fp, b.fp); } // splits a section into pieces, as specified by an array of doubles, mutating the section to @@ -203,7 +203,7 @@ std::vector<boost::shared_ptr<Section> > split_section(boost::shared_ptr<Section s->t = cuts[1]; s->tp = s->curve.get(ps)(cuts[1]); - assert(Point::LexOrderRt(d)(s->fp, s->tp)); + assert(Point::LexLessRt(d)(s->fp, s->tp)); ret.reserve(cuts.size() - 2); for(int i = cuts.size() - 1; i > 1; i--) ret.push_back(boost::shared_ptr<Section>(new Section(s->curve, cuts[i-1], cuts[i], ps, d))); @@ -380,7 +380,7 @@ TopoGraph::TopoGraph(PathVector const &ps, Dim2 d, double t) : dim(d), tol(t) { //find all sections to remove for(int i = context.size() - 1; i >= 0; i--) { boost::shared_ptr<Section> sec = context[i].section; - if(Point::LexOrderRt(d)(lim, sec->tp)) { + if(Point::LexLessRt(d)(lim, sec->tp)) { //sec->tp is less than or equal to lim if(context[i].to_vert == -1) { //we need to create a new vertex; add everything that enters it @@ -639,6 +639,7 @@ void remove_area_whiskers(Areas &areas) { Path area_to_path(PathVector const &ps, Area const &area) { Path ret; + ret.setStitching(true); if(area.size() == 0) return ret; Point prev = area[0]->fp; for(unsigned i = 0; i < area.size(); i++) { @@ -646,17 +647,18 @@ Path area_to_path(PathVector const &ps, Area const &area) { Curve *curv = area[i]->curve.get(ps).portion( forward ? area[i]->f : area[i]->t, forward ? area[i]->t : area[i]->f); - ret.append(*curv, Path::STITCH_DISCONTINUOUS); + ret.append(*curv); delete curv; prev = forward ? area[i]->tp : area[i]->fp; } + ret.setStitching(false); return ret; } PathVector areas_to_paths(PathVector const &ps, Areas const &areas) { - std::vector<Path> ret; - ret.reserve(areas.size()); - for(unsigned i = 0; i < areas.size(); i++) + PathVector ret; + //ret.reserve(areas.size()); + for(unsigned i = 0; i < areas.size(); ++i) ret.push_back(area_to_path(ps, areas[i])); return ret; } diff --git a/src/2geom/toposweep.h b/src/2geom/toposweep.h index b6a55b154..7faf890e1 100644 --- a/src/2geom/toposweep.h +++ b/src/2geom/toposweep.h @@ -1,8 +1,7 @@ - /** * \file * \brief TopoSweep - topology / graph representation of a PathVector, for boolean operations and related tasks - * + *//* * Authors: * Michael Sloan <mgsloan at gmail.com> * Nathan Hurst <njhurst at njhurst.com> @@ -33,8 +32,8 @@ * the specific language governing rights and limitations. */ -#ifndef SEEN_GEOM_TOPOSWEEP_H -#define SEEN_GEOM_TOPOSWEEP_H +#ifndef LIB2GEOM_SEEN_TOPOSWEEP_H +#define LIB2GEOM_SEEN_TOPOSWEEP_H #include <2geom/coord.h> #include <2geom/point.h> @@ -69,10 +68,11 @@ struct Section { Section(CurveIx cix, double fd, double td, Point fdp, Point tdp) : curve(cix), f(fd), t(td), fp(fdp), tp(tdp) { } Section(CurveIx cix, double fd, double td, PathVector ps, Dim2 d) : curve(cix), f(fd), t(td) { fp = curve.get(ps).pointAt(f), tp = curve.get(ps).pointAt(t); - if (Point::LexOrderRt(d)(tp, fp)) { + if (Point::LexLessRt(d)(tp, fp)) { //swap from and to, since tp is left or above fp - std::swap(f, t); - std::swap(fp, tp); + using std::swap; + swap(f, t); + swap(fp, tp); } } Rect bbox() const { return Rect(fp, tp); } @@ -167,7 +167,7 @@ struct SweepSorter { Dim2 dim; SweepSorter(Dim2 d) : dim(d) {} bool operator()(const Section &a, const Section &b) const { - return Point::LexOrderRt(dim)(a.fp, b.fp); + return Point::LexLessRt(dim)(a.fp, b.fp); } }; @@ -208,7 +208,7 @@ Areas filter_areas(PathVector const &ps, Areas const & areas, Z const &z) { } // end namespace Geom -#endif // SEEN_GEOM_TOPOSWEEP_H +#endif // LIB2GEOM_SEEN_TOPOSWEEP_H /* Local Variables: diff --git a/src/2geom/transforms.cpp b/src/2geom/transforms.cpp index 091079d5a..41d395297 100644 --- a/src/2geom/transforms.cpp +++ b/src/2geom/transforms.cpp @@ -139,6 +139,24 @@ Affine &Affine::operator*=(Zoom const &z) { return *this; } +Affine Rotate::around(Point const &p, Coord angle) +{ + Affine result = Translate(-p) * Rotate(angle) * Translate(p); + return result; +} + +Affine reflection(Point const & vector, Point const & origin) +{ + Geom::Point vn = unit_vector(vector); + Coord cx2 = vn[X] * vn[X]; + Coord cy2 = vn[Y] * vn[Y]; + Coord c2xy = 2 * vn[X] * vn[Y]; + Affine mirror ( cx2 - cy2, c2xy, + c2xy, cy2 - cx2, + 0, 0 ); + return Translate(-origin) * mirror * Translate(origin); +} + // this checks whether the requirements of TransformConcept are satisfied for all transforms. // if you add a new transform type, include it here! void check_transforms() @@ -173,14 +191,6 @@ void check_transforms() m = z * t; m = z * s; m = z * r; m = z * h; m = z * v; m = z * z; } -Affine reflection(Point const & vector, Point const & origin) { - Geom::Point vec_norm = unit_vector(vector); - Affine mirror ( vec_norm[X]*vec_norm[X] - vec_norm[Y]*vec_norm[Y], 2 * vec_norm[X] * vec_norm[Y] , - 2 * vec_norm[X] * vec_norm[Y], vec_norm[Y]*vec_norm[Y] - vec_norm[X]*vec_norm[X] , - 0 ,0 ); - return Translate(-origin) * mirror * Translate(origin); -} - } // namespace Geom /* diff --git a/src/2geom/transforms.h b/src/2geom/transforms.h index 7f5635747..de4e6871f 100644 --- a/src/2geom/transforms.h +++ b/src/2geom/transforms.h @@ -40,6 +40,7 @@ #include <2geom/forward.h> #include <2geom/affine.h> #include <2geom/angle.h> +#include <boost/concept/assert.hpp> namespace Geom { @@ -95,6 +96,7 @@ public: * @ingroup Transforms */ template <typename T> T pow(T const &t, int n) { + BOOST_CONCEPT_ASSERT((TransformConcept<T>)); if (n == 0) return T::identity(); T result(T::identity()); T x(n < 0 ? t.inverse() : t); @@ -199,6 +201,7 @@ public: /** @brief Get the characteristic vector of the rotation. * @return A vector that would be obtained by applying this transform to the X versor. */ Point vector() const { return vec; } + Coord angle() const { return atan2(vec); } Coord operator[](Dim2 dim) const { return vec[dim]; } Coord operator[](unsigned dim) const { return vec[dim]; } Rotate &operator*=(Rotate const &o) { vec *= o; return *this; } @@ -216,6 +219,7 @@ public: Coord rad = (deg / 180.0) * M_PI; return Rotate(rad); } + static Affine around(Point const &p, Coord angle); friend class Point; }; @@ -343,6 +347,8 @@ inline Translate pow(Translate const &t, int n) { /** @brief Reflects objects about line. * The line, defined by a vector along the line and a point on it, acts as a mirror. + * @ingroup Transforms + * @see Line::reflection() */ Affine reflection(Point const & vector, Point const & origin); diff --git a/src/2geom/utils.h b/src/2geom/utils.h index fe955dd41..bc0ad74b8 100644 --- a/src/2geom/utils.h +++ b/src/2geom/utils.h @@ -30,11 +30,12 @@ * */ -#ifndef SEEN_LIB2GEOM_UTILS_H -#define SEEN_LIB2GEOM_UTILS_H +#ifndef LIB2GEOM_SEEN_UTILS_H +#define LIB2GEOM_SEEN_UTILS_H #include <cstddef> #include <vector> +#include <boost/operators.hpp> namespace Geom { @@ -59,9 +60,20 @@ struct MultipliableNoncommutative : B } }; +/** @brief Null output iterator + * Use this if you want to discard a result returned through an output iterator. */ +struct NullIterator + : public boost::output_iterator_helper<NullIterator> +{ + NullIterator() {} + + template <typename T> + void operator=(T const &v) {} +}; + } // end namespace Geom -#endif // SEEN_LIB2GEOM_UTILS_H +#endif // LIB2GEOM_SEEN_UTILS_H /* Local Variables: diff --git a/src/2geom/viewbox.cpp b/src/2geom/viewbox.cpp new file mode 100644 index 000000000..69bd0c487 --- /dev/null +++ b/src/2geom/viewbox.cpp @@ -0,0 +1,133 @@ +/** + * \file + * \brief Convenience class for SVG viewBox handling + *//* + * Authors: + * Krzysztof Kosiński <tweenk.pl@gmail.com> + * + * Copyright (C) 2013 Authors + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + */ + +#include <2geom/transforms.h> +#include <2geom/viewbox.h> + +namespace Geom { + +/** Convert an align specification to coordinate fractions. */ +Point align_factors(Align g) { + Point p; + switch (g) { + case ALIGN_XMIN_YMIN: + p[X] = 0.0; + p[Y] = 0.0; + break; + case ALIGN_XMID_YMIN: + p[X] = 0.5; + p[Y] = 0.0; + break; + case ALIGN_XMAX_YMIN: + p[X] = 1.0; + p[Y] = 0.0; + break; + case ALIGN_XMIN_YMID: + p[X] = 0.0; + p[Y] = 0.5; + break; + case ALIGN_XMID_YMID: + p[X] = 0.5; + p[Y] = 0.5; + break; + case ALIGN_XMAX_YMID: + p[X] = 1.0; + p[Y] = 0.5; + break; + case ALIGN_XMIN_YMAX: + p[X] = 0.0; + p[Y] = 1.0; + break; + case ALIGN_XMID_YMAX: + p[X] = 0.5; + p[Y] = 1.0; + break; + case ALIGN_XMAX_YMAX: + p[X] = 1.0; + p[Y] = 1.0; + break; + default: + break; + } + return p; +} + +/** Obtain transformation from the viewbox to the specified viewport. */ +Affine ViewBox::transformTo(Geom::Rect const &viewport) const +{ + if (!_box) { + return Geom::Affine::identity(); + } + + // 1. translate viewbox to origin + Geom::Affine total = Translate(-_box->min()); + + // 2. compute scale + Geom::Point vdims = viewport.dimensions(); + Geom::Point bdims = _box->dimensions(); + Geom::Scale scale(vdims[X] / bdims[X], vdims[Y] / bdims[Y]); + + if (_align == ALIGN_NONE) { + // apply non-uniform scale + // = Scale(_box->dimensions()).inverse() * Scale(viewport.dimensions()) + total *= scale * Translate(viewport.min()); + } else { + double uscale = 0; + if (_expansion == EXPANSION_MEET) { + uscale = std::min(scale[X], scale[Y]); + } else { + uscale = std::max(scale[X], scale[Y]); + } + scale = Scale(uscale); + + // compute offset for align + Geom::Point offset = bdims * scale - vdims; + offset *= Scale(align_factors(_align)); + total *= Translate(-offset); + } + + return total; +} + +} // namespace Geom + +/* + 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/2geom/viewbox.h b/src/2geom/viewbox.h new file mode 100644 index 000000000..81f59ee36 --- /dev/null +++ b/src/2geom/viewbox.h @@ -0,0 +1,102 @@ +/** + * \file + * \brief Convenience class for SVG viewBox handling + *//* + * Authors: + * Krzysztof Kosiński <tweenk.pl@gmail.com> + * + * Copyright (C) 2013 Authors + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + */ + +#ifndef LIB2GEOM_SEEN_VIEWBOX_H +#define LIB2GEOM_SEEN_VIEWBOX_H + +#include <2geom/affine.h> +#include <2geom/rect.h> + +namespace Geom { + +/** Values for the <align> parameter of preserveAspectRatio. + * See: http://www.w3.org/TR/SVG/coords.html#PreserveAspectRatioAttribute */ +enum Align { + ALIGN_NONE, + ALIGN_XMIN_YMIN, + ALIGN_XMID_YMIN, + ALIGN_XMAX_YMIN, + ALIGN_XMIN_YMID, + ALIGN_XMID_YMID, + ALIGN_XMAX_YMID, + ALIGN_XMIN_YMAX, + ALIGN_XMID_YMAX, + ALIGN_XMAX_YMAX +}; + +/** Values for the <meetOrSlice> parameter of preserveAspectRatio. + * See: http://www.w3.org/TR/SVG/coords.html#PreserveAspectRatioAttribute */ +enum Expansion { + EXPANSION_MEET, + EXPANSION_SLICE +}; + +Point align_factors(Align align); + +class ViewBox { + OptRect _box; + Align _align; + Expansion _expansion; + +public: + explicit ViewBox(OptRect const &r = OptRect(), Align a = ALIGN_XMID_YMID, Expansion ex = EXPANSION_MEET) + : _box(r) + , _align(a) + , _expansion(ex) + {} + + void setBox(OptRect const &r) { _box = r; } + void setAlign(Align a) { _align = a; } + void setExpansion(Expansion ex) { _expansion = ex; } + OptRect const &box() const { return _box; } + Align align() const { return _align; } + Expansion expansion() const { return _expansion; } + + /** Obtain transformation from the viewbox to the specified viewport. */ + Affine transformTo(Geom::Rect const &viewport) const; +}; + +} // namespace Geom + +#endif // !LIB2GEOM_SEEN_VIEWBOX_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/CMakeLists.txt b/src/CMakeLists.txt index 27c5e49db..c416b0dea 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -166,6 +166,9 @@ set(sp_SRC sp-style-elem.h sp-switch.h sp-symbol.h + sp-tag.h + sp-tag-use.h + sp-tag-use-reference.h sp-text.h sp-textpath.h sp-title.h @@ -423,6 +426,10 @@ if(WIN32) #deptool.cpp winconsole.cpp winmain.cpp + + # ------- + # Headers + registrytool.h ) endif() @@ -519,7 +526,7 @@ add_dependencies(inkscape inkscape_version) target_link_libraries(inkscape # order from automake #sp_LIB - nrtype_LIB + #nrtype_LIB #inkscape_LIB #sp_LIB # annoying, we need both! diff --git a/src/Makefile.am b/src/Makefile.am index cfbbd4015..7a37f13e8 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -23,7 +23,6 @@ endif noinst_LIBRARIES = \ - libinkscape.a \ libcroco/libcroco.a \ libavoid/libavoid.a \ $(internal_GDL) \ @@ -36,6 +35,7 @@ noinst_LIBRARIES = \ libdepixelize/libdepixelize.a \ util/libutil.a \ libinkversion.a +# libinkscape.a all_libs = \ $(noinst_LIBRARIES) \ @@ -198,15 +198,15 @@ DISTCLEANFILES = \ # ################################################ # this should speed up the build -libinkscape_a_SOURCES = $(ink_common_sources) +#libinkscape_a_SOURCES = $(ink_common_sources) -inkscape_SOURCES += main.cpp $(win32_sources) +inkscape_SOURCES += main.cpp $(ink_common_sources) $(win32_sources) inkscape_LDADD = $(all_libs) inkscape_LDFLAGS = $(kdeldflags) $(mwindows) -inkview_SOURCES += inkview.cpp $(win32_sources) +inkview_SOURCES += inkview.cpp $(ink_common_sources) $(win32_sources) inkview_LDADD = $(all_libs) -inkview_LDFLAGS = $(mwindows) +inkview_LDFLAGS = $(mwindows) # ################################################ # VERSION REPORTING diff --git a/src/attributes-test.h b/src/attributes-test.h index 7379e4e85..411304ec3 100644 --- a/src/attributes-test.h +++ b/src/attributes-test.h @@ -40,6 +40,10 @@ public: I've added these manually. SVG 2: white-space, shape-inside, shape-outside, shape-padding, shape-margin + SVG 2: text-decoration-fill, text-decoration-stroke + SVG 2: solid-color, solid-opacity + SVG 2: Hatches and Meshes + CSS 3: font-variant-xxx, font-feature-settings */ struct {char const *attr; bool supported;} const all_attrs[] = { {"attributeName", true}, @@ -124,11 +128,18 @@ struct {char const *attr; bool supported;} const all_attrs[] = { {"flood-color", true}, {"flood-opacity", true}, {"font-family", true}, + {"font-feature-settings", true}, {"font-size", true}, {"font-size-adjust", true}, {"font-stretch", true}, {"font-style", true}, {"font-variant", true}, + {"font-variant-ligatures", true}, + {"font-variant-position", true}, + {"font-variant-caps", true}, + {"font-variant-numeric", true}, + {"font-variant-east-asian", true}, + {"font-variant-alternates", true}, {"font-weight", true}, {"format", false}, {"from", true}, @@ -288,6 +299,8 @@ struct {char const *attr; bool supported;} const all_attrs[] = { {"text-decoration-line", true}, {"text-decoration-style", true}, {"text-decoration-color", true}, + {"text-decoration-fill", true}, + {"text-decoration-stroke", true}, {"text-indent", true}, {"text-rendering", true}, {"text-transform", true}, @@ -381,8 +394,11 @@ struct {char const *attr; bool supported;} const all_attrs[] = { {"inkscape:pageopacity", true}, {"inkscape:pageshadow", true}, {"inkscape:path-effect", true}, + // SPItem {"inkscape:transform-center-x", true}, {"inkscape:transform-center-y", true}, + {"inkscape:highlight-color", true}, + // Namedview {"inkscape:zoom", true}, {"inkscape:cx", true}, {"inkscape:cy", true}, @@ -392,6 +408,7 @@ struct {char const *attr; bool supported;} const all_attrs[] = { {"inkscape:window-y", true}, {"inkscape:window-maximized", true}, {"inkscape:current-layer", true}, + // Connector tool {"inkscape:connector-type", true}, {"inkscape:connection-start", true}, {"inkscape:connection-end", true}, @@ -401,10 +418,12 @@ struct {char const *attr; bool supported;} const all_attrs[] = { {"inkscape:connector-curvature", true}, {"inkscape:connector-avoid", true}, {"inkscape:connector-spacing", true}, + // Ellipse, Spiral, Star {"sodipodi:cx", true}, {"sodipodi:cy", true}, {"sodipodi:rx", true}, {"sodipodi:ry", true}, + // Box tool {"inkscape:perspectiveID", true}, {"inkscape:corner0", true}, {"inkscape:corner7", true}, @@ -414,6 +433,7 @@ struct {char const *attr; bool supported;} const all_attrs[] = { {"inkscape:vp_y", true}, {"inkscape:vp_z", true}, {"inkscape:persp3d-origin", true}, + // Star tool {"sodipodi:start", true}, {"sodipodi:end", true}, {"sodipodi:open", true}, @@ -446,9 +466,19 @@ struct {char const *attr; bool supported;} const all_attrs[] = { {"inkscape:layoutOptions", true}, {"osb:paint", true}, + /* SPSolidColor" */ + {"solid-color", true}, + {"solid-opacity", true}, + /* SPMeshPatch */ {"tensor", true}, + /* SPHash */ + {"hatchUnits", true}, + {"hatchContentUnits", true}, + {"hatchTransform", true}, + {"pitch", true}, + /* SPNamedView */ {"fit-margin-top", true}, {"fit-margin-left", true}, @@ -485,8 +515,10 @@ struct {char const *attr; bool supported;} const all_attrs[] = { {"pagecolor", true}, /* SPGuide */ - {"position", true} + {"position", true}, + /* SPTag */ + {"inkscape:expanded", true} }; diff --git a/src/attributes.cpp b/src/attributes.cpp index 568f0528a..af19360c1 100644 --- a/src/attributes.cpp +++ b/src/attributes.cpp @@ -419,6 +419,16 @@ static SPStyleProp const props[] = { {SP_PROP_FONT_STYLE, "font-style"}, {SP_PROP_FONT_VARIANT, "font-variant"}, {SP_PROP_FONT_WEIGHT, "font-weight"}, + + /* Font Variants CSS 3 */ + {SP_PROP_FONT_VARIANT_LIGATURES, "font-variant-ligatures"}, + {SP_PROP_FONT_VARIANT_POSITION, "font-variant-position"}, + {SP_PROP_FONT_VARIANT_CAPS, "font-variant-caps"}, + {SP_PROP_FONT_VARIANT_NUMERIC, "font-variant-numeric"}, + {SP_PROP_FONT_VARIANT_ALTERNATES, "font-variant-alternates"}, + {SP_PROP_FONT_VARIANT_EAST_ASIAN, "font-variant-east-asian"}, + {SP_PROP_FONT_FEATURE_SETTINGS, "font-feature-settings"}, + /* Text */ {SP_PROP_TEXT_INDENT, "text-indent"}, {SP_PROP_TEXT_ALIGN, "text-align"}, @@ -426,6 +436,7 @@ static SPStyleProp const props[] = { {SP_PROP_LETTER_SPACING, "letter-spacing"}, {SP_PROP_WORD_SPACING, "word-spacing"}, {SP_PROP_TEXT_TRANSFORM, "text-transform"}, + /* Text (css3) */ {SP_PROP_DIRECTION, "direction"}, {SP_PROP_BLOCK_PROGRESSION, "block-progression"}, @@ -439,16 +450,21 @@ static SPStyleProp const props[] = { {SP_PROP_KERNING, "kerning"}, {SP_PROP_TEXT_ANCHOR, "text-anchor"}, {SP_PROP_WHITE_SPACE, "white-space"}, + /* SVG 2 Text Wrapping */ {SP_PROP_SHAPE_INSIDE, "shape-inside"}, {SP_PROP_SHAPE_OUTSIDE, "shape-outside"}, {SP_PROP_SHAPE_PADDING, "shape-padding"}, {SP_PROP_SHAPE_MARGIN, "shape-margin"}, + /* Text Decoration */ - {SP_PROP_TEXT_DECORATION, "text-decoration"}, - {SP_PROP_TEXT_DECORATION_LINE, "text-decoration-line"}, - {SP_PROP_TEXT_DECORATION_STYLE,"text-decoration-style"}, - {SP_PROP_TEXT_DECORATION_COLOR,"text-decoration-color"}, + {SP_PROP_TEXT_DECORATION, "text-decoration"}, + {SP_PROP_TEXT_DECORATION_LINE, "text-decoration-line"}, + {SP_PROP_TEXT_DECORATION_STYLE, "text-decoration-style"}, + {SP_PROP_TEXT_DECORATION_COLOR, "text-decoration-color"}, + {SP_PROP_TEXT_DECORATION_FILL, "text-decoration-fill"}, + {SP_PROP_TEXT_DECORATION_STROKE,"text-decoration-stroke"}, + /* Misc */ {SP_PROP_CLIP, "clip"}, {SP_PROP_COLOR, "color"}, diff --git a/src/attributes.h b/src/attributes.h index 91c8868f9..7d6ea70a0 100644 --- a/src/attributes.h +++ b/src/attributes.h @@ -424,6 +424,15 @@ enum SPAttributeEnum { SP_PROP_FONT_VARIANT, SP_PROP_FONT_WEIGHT, + /* Font Variants CSS 3 */ + SP_PROP_FONT_VARIANT_LIGATURES, + SP_PROP_FONT_VARIANT_POSITION, + SP_PROP_FONT_VARIANT_CAPS, + SP_PROP_FONT_VARIANT_NUMERIC, + SP_PROP_FONT_VARIANT_ALTERNATES, + SP_PROP_FONT_VARIANT_EAST_ASIAN, + SP_PROP_FONT_FEATURE_SETTINGS, + /* Text Layout */ SP_PROP_TEXT_INDENT, SP_PROP_TEXT_ALIGN, @@ -446,16 +455,20 @@ enum SPAttributeEnum { SP_PROP_TEXT_ANCHOR, SP_PROP_WHITE_SPACE, + /* SVG 2 Text Wrapping */ SP_PROP_SHAPE_INSIDE, SP_PROP_SHAPE_OUTSIDE, SP_PROP_SHAPE_PADDING, SP_PROP_SHAPE_MARGIN, - /* Text Decoration */ - SP_PROP_TEXT_DECORATION, /* SVG 1 underline etc.( no color or style) OR SVG2 with _LINE, _STYLE, _COLOR values */ - SP_PROP_TEXT_DECORATION_LINE, /* SVG 2 underline etc. */ - SP_PROP_TEXT_DECORATION_STYLE, /* SVG 2 proposed solid [SVG 1], dotted, etc.)*/ - SP_PROP_TEXT_DECORATION_COLOR, /* SVG 2 proposed same as text [SVG 1], specified*/ + /* Text Decoration CSS 2/CSS 3 Shorthand */ + SP_PROP_TEXT_DECORATION, + /* Text Decoration CSS 3/SVG 2 */ + SP_PROP_TEXT_DECORATION_LINE, + SP_PROP_TEXT_DECORATION_STYLE, + SP_PROP_TEXT_DECORATION_COLOR, + SP_PROP_TEXT_DECORATION_FILL, + SP_PROP_TEXT_DECORATION_STROKE, /* Misc */ SP_PROP_CLIP, diff --git a/src/color-profile.cpp b/src/color-profile.cpp index 85a6fa876..690a72654 100644 --- a/src/color-profile.cpp +++ b/src/color-profile.cpp @@ -313,7 +313,7 @@ void ColorProfile::set(unsigned key, gchar const *value) { //# 1. Get complete URI of document gchar const *docbase = doc->getURI(); - gchar* escaped = g_uri_escape_string(this->href, "!*'();:@=+$,/?#[]", TRUE); + gchar* escaped = g_uri_escape_string(this->href, "!*'();:@=+$,/?#", TRUE); //g_message("docbase:%s\n", docbase); //org::w3c::dom::URI docUri(docbase); diff --git a/src/conn-avoid-ref.cpp b/src/conn-avoid-ref.cpp index c13b9a5d3..ec9aba793 100644 --- a/src/conn-avoid-ref.cpp +++ b/src/conn-avoid-ref.cpp @@ -19,7 +19,7 @@ #include "display/curve.h" #include "2geom/line.h" #include "2geom/crossing.h" -#include "2geom/convex-cover.h" +#include "2geom/convex-hull.h" #include "helper/geom-curves.h" #include "svg/stringstream.h" #include "conn-avoid-ref.h" @@ -252,8 +252,9 @@ static std::vector<Geom::Point> approxItemWithPoints(SPItem const *item, const G { SPGroup* group = SP_GROUP(item); // consider all first-order children - for (GSList const* i = sp_item_group_item_list(group); i != NULL; i = i->next) { - SPItem* child_item = SP_ITEM(i->data); + std::vector<SPItem*> itemlist = sp_item_group_item_list(group); + for (std::vector<SPItem*>::const_iterator i = itemlist.begin(); i != itemlist.end(); i++) { + SPItem* child_item = *i; std::vector<Geom::Point> child_points = approxItemWithPoints(child_item, item_transform * child_item->transform); poly_points.insert(poly_points.end(), child_points.begin(), child_points.end()); } @@ -296,7 +297,7 @@ static Avoid::Polygon avoid_item_poly(SPItem const *item) Geom::Line prev_parallel_hull_edge; prev_parallel_hull_edge.setOrigin(hull_edge.origin()+hull_edge.versor().ccw()*spacing); prev_parallel_hull_edge.setVersor(hull_edge.versor()); - int hull_size = hull.boundary.size(); + int hull_size = hull.size(); for (int i = 0; i < hull_size; ++i) { hull_edge.setPoints(hull[i], hull[i+1]); diff --git a/src/desktop-events.cpp b/src/desktop-events.cpp index e80e5f6c1..b685dacbf 100644 --- a/src/desktop-events.cpp +++ b/src/desktop-events.cpp @@ -354,7 +354,7 @@ gint sp_dt_guide_event(SPCanvasItem *item, GdkEvent *event, gpointer data) // the origin should still be constrained to the guide. So let's do // that explicitly first: Geom::Line line(guide->getPoint(), guide->angle()); - Geom::Coord t = line.nearestPoint(motion_dt); + Geom::Coord t = line.nearestTime(motion_dt); motion_dt = line.pointAt(t); if (!(event->motion.state & GDK_SHIFT_MASK)) { m.guideConstrainedSnap(motion_dt, *guide); @@ -435,7 +435,7 @@ gint sp_dt_guide_event(SPCanvasItem *item, GdkEvent *event, gpointer data) // the origin should still be constrained to the guide. So let's // do that explicitly first: Geom::Line line(guide->getPoint(), guide->angle()); - Geom::Coord t = line.nearestPoint(event_dt); + Geom::Coord t = line.nearestTime(event_dt); event_dt = line.pointAt(t); if (!(event->button.state & GDK_SHIFT_MASK)) { m.guideConstrainedSnap(event_dt, *guide); diff --git a/src/desktop-style.cpp b/src/desktop-style.cpp index c713e0d61..02c18339b 100644 --- a/src/desktop-style.cpp +++ b/src/desktop-style.cpp @@ -194,10 +194,10 @@ sp_desktop_set_style(SPDesktop *desktop, SPCSSAttr *css, bool change, bool write sp_repr_css_merge(css_write, css); sp_css_attr_unset_uris(css_write); prefs->mergeStyle("/desktop/style", css_write); - - for (const GSList *i = desktop->selection->itemList(); i != NULL; i = i->next) { + std::vector<SPItem*> const itemlist = desktop->selection->itemList(); + for (std::vector<SPItem*>::const_iterator i = itemlist.begin(); i!= itemlist.end(); i++) { /* last used styles for 3D box faces are stored separately */ - SPObject *obj = reinterpret_cast<SPObject *>(i->data); // TODO unsafe until Selection is refactored. + SPObject *obj = *i; Box3DSide *side = dynamic_cast<Box3DSide *>(obj); if (side) { const char * descr = box3d_side_axes_string(side); @@ -234,8 +234,9 @@ sp_desktop_set_style(SPDesktop *desktop, SPCSSAttr *css, bool change, bool write sp_repr_css_merge(css_no_text, css); css_no_text = sp_css_attr_unset_text(css_no_text); - for (GSList const *i = desktop->selection->itemList(); i != NULL; i = i->next) { - SPItem *item = reinterpret_cast<SPItem *>(i->data); + std::vector<SPItem*> const itemlist = desktop->selection->itemList(); + for (std::vector<SPItem*>::const_iterator i = itemlist.begin(); i!= itemlist.end(); i++) { + SPItem *item = *i; // If not text, don't apply text attributes (can a group have text attributes? Yes! FIXME) if (isTextualItem(item)) { @@ -438,18 +439,16 @@ sp_desktop_get_font_size_tool(SPDesktop *desktop) /** Determine average stroke width, simple method */ // see TODO in dialogs/stroke-style.cpp on how to get rid of this eventually gdouble -stroke_average_width (GSList const *objects) +stroke_average_width (const std::vector<SPItem*> &objects) { - if (g_slist_length ((GSList *) objects) == 0) + if (objects.empty()) return Geom::infinity(); gdouble avgwidth = 0.0; bool notstroked = true; int n_notstroked = 0; - - for (GSList const *l = objects; l != NULL; l = l->next) { - SPObject *obj = reinterpret_cast<SPObject *>(l->data); - SPItem *item = dynamic_cast<SPItem *>(obj); + for (std::vector<SPItem*>::const_iterator i = objects.begin(); i != objects.end(); i++) { + SPItem *item = *i; if (!item) { continue; } @@ -471,7 +470,7 @@ stroke_average_width (GSList const *objects) if (notstroked) return Geom::infinity(); - return avgwidth / (g_slist_length ((GSList *) objects) - n_notstroked); + return avgwidth / (objects.size() - n_notstroked); } static bool vectorsClose( std::vector<double> const &lhs, std::vector<double> const &rhs ) @@ -492,9 +491,9 @@ static bool vectorsClose( std::vector<double> const &lhs, std::vector<double> co * Write to style_res the average fill or stroke of list of objects, if applicable. */ int -objects_query_fillstroke (GSList *objects, SPStyle *style_res, bool const isfill) +objects_query_fillstroke (const std::vector<SPItem*> &objects, SPStyle *style_res, bool const isfill) { - if (g_slist_length(objects) == 0) { + if (objects.empty()) { /* No objects, set empty */ return QUERY_STYLE_NOTHING; } @@ -514,8 +513,8 @@ objects_query_fillstroke (GSList *objects, SPStyle *style_res, bool const isfill prev[0] = prev[1] = prev[2] = 0.0; bool same_color = true; - for (GSList const *i = objects; i != NULL; i = i->next) { - SPObject *obj = reinterpret_cast<SPObject *>(i->data); + for (std::vector<SPItem*>::const_iterator i = objects.begin(); i!= objects.end(); i++) { + SPObject *obj = *i; if (!obj) { continue; } @@ -577,8 +576,8 @@ objects_query_fillstroke (GSList *objects, SPStyle *style_res, bool const isfill return QUERY_STYLE_MULTIPLE_DIFFERENT; // different kind of server } - SPPattern *pat = pattern_getroot (pattern); - SPPattern *pat_res = pattern_getroot (pattern_res); + SPPattern *pat = SP_PATTERN (server)->rootPattern(); + SPPattern *pat_res = SP_PATTERN (server_res)->rootPattern(); if (pat_res != pat) { return QUERY_STYLE_MULTIPLE_DIFFERENT; // different pattern roots } @@ -674,7 +673,7 @@ objects_query_fillstroke (GSList *objects, SPStyle *style_res, bool const isfill } // Not color - if (g_slist_length(objects) > 1) { + if (objects.size() > 1) { return QUERY_STYLE_MULTIPLE_SAME; } else { return QUERY_STYLE_SINGLE; @@ -685,9 +684,9 @@ objects_query_fillstroke (GSList *objects, SPStyle *style_res, bool const isfill * Write to style_res the average opacity of a list of objects. */ int -objects_query_opacity (GSList *objects, SPStyle *style_res) +objects_query_opacity (const std::vector<SPItem*> &objects, SPStyle *style_res) { - if (g_slist_length(objects) == 0) { + if (objects.empty()) { /* No objects, set empty */ return QUERY_STYLE_NOTHING; } @@ -698,8 +697,8 @@ objects_query_opacity (GSList *objects, SPStyle *style_res) guint opacity_items = 0; - for (GSList const *i = objects; i != NULL; i = i->next) { - SPObject *obj = reinterpret_cast<SPObject *>(i->data); + for (std::vector<SPItem*>::const_iterator i = objects.begin(); i != objects.end(); i++) { + SPObject *obj = *i; if (!obj) { continue; } @@ -739,9 +738,9 @@ objects_query_opacity (GSList *objects, SPStyle *style_res) * Write to style_res the average stroke width of a list of objects. */ int -objects_query_strokewidth (GSList *objects, SPStyle *style_res) +objects_query_strokewidth (const std::vector<SPItem*> &objects, SPStyle *style_res) { - if (g_slist_length(objects) == 0) { + if (objects.empty()) { /* No objects, set empty */ return QUERY_STYLE_NOTHING; } @@ -754,8 +753,8 @@ objects_query_strokewidth (GSList *objects, SPStyle *style_res) int n_stroked = 0; - for (GSList const *i = objects; i != NULL; i = i->next) { - SPObject *obj = reinterpret_cast<SPObject *>(i->data); + for (std::vector<SPItem*>::const_iterator i = objects.begin(); i != objects.end(); i++) { + SPObject *obj = *i; if (!obj) { continue; } @@ -815,9 +814,9 @@ objects_query_strokewidth (GSList *objects, SPStyle *style_res) * Write to style_res the average miter limit of a list of objects. */ int -objects_query_miterlimit (GSList *objects, SPStyle *style_res) +objects_query_miterlimit (const std::vector<SPItem*> &objects, SPStyle *style_res) { - if (g_slist_length(objects) == 0) { + if (objects.empty()) { /* No objects, set empty */ return QUERY_STYLE_NOTHING; } @@ -828,8 +827,8 @@ objects_query_miterlimit (GSList *objects, SPStyle *style_res) gdouble prev_ml = -1; bool same_ml = true; - for (GSList const *i = objects; i != NULL; i = i->next) { - SPObject *obj = reinterpret_cast<SPObject *>(i->data); + for (std::vector<SPItem*>::const_iterator i = objects.begin(); i != objects.end(); i++) { + SPObject *obj = *i; if (!dynamic_cast<SPItem *>(obj)) { continue; } @@ -875,9 +874,9 @@ objects_query_miterlimit (GSList *objects, SPStyle *style_res) * Write to style_res the stroke cap of a list of objects. */ int -objects_query_strokecap (GSList *objects, SPStyle *style_res) +objects_query_strokecap (const std::vector<SPItem*> &objects, SPStyle *style_res) { - if (g_slist_length(objects) == 0) { + if (objects.empty()) { /* No objects, set empty */ return QUERY_STYLE_NOTHING; } @@ -887,8 +886,8 @@ objects_query_strokecap (GSList *objects, SPStyle *style_res) bool same_cap = true; int n_stroked = 0; - for (GSList const *i = objects; i != NULL; i = i->next) { - SPObject *obj = reinterpret_cast<SPObject *>(i->data); + for (std::vector<SPItem*>::const_iterator i = objects.begin(); i != objects.end(); i++) { + SPObject *obj = *i; if (!dynamic_cast<SPItem *>(obj)) { continue; } @@ -929,9 +928,9 @@ objects_query_strokecap (GSList *objects, SPStyle *style_res) * Write to style_res the stroke join of a list of objects. */ int -objects_query_strokejoin (GSList *objects, SPStyle *style_res) +objects_query_strokejoin (const std::vector<SPItem*> &objects, SPStyle *style_res) { - if (g_slist_length(objects) == 0) { + if (objects.empty()) { /* No objects, set empty */ return QUERY_STYLE_NOTHING; } @@ -941,8 +940,8 @@ objects_query_strokejoin (GSList *objects, SPStyle *style_res) bool same_join = true; int n_stroked = 0; - for (GSList const *i = objects; i != NULL; i = i->next) { - SPObject *obj = reinterpret_cast<SPObject *>(i->data); + for (std::vector<SPItem*>::const_iterator i = objects.begin(); i != objects.end(); i++) { + SPObject *obj = *i; if (!dynamic_cast<SPItem *>(obj)) { continue; } @@ -984,7 +983,7 @@ objects_query_strokejoin (GSList *objects, SPStyle *style_res) * Write to style_res the average font size and spacing of objects. */ int -objects_query_fontnumbers (GSList *objects, SPStyle *style_res) +objects_query_fontnumbers (const std::vector<SPItem*> &objects, SPStyle *style_res) { bool different = false; @@ -1004,8 +1003,8 @@ objects_query_fontnumbers (GSList *objects, SPStyle *style_res) int texts = 0; int no_size = 0; - for (GSList const *i = objects; i != NULL; i = i->next) { - SPObject *obj = reinterpret_cast<SPObject *>(i->data); + for (std::vector<SPItem*>::const_iterator i = objects.begin(); i != objects.end(); i++) { + SPObject *obj = *i; if (!isTextualItem(obj)) { continue; @@ -1116,15 +1115,15 @@ objects_query_fontnumbers (GSList *objects, SPStyle *style_res) * Write to style_res the average font style of objects. */ int -objects_query_fontstyle (GSList *objects, SPStyle *style_res) +objects_query_fontstyle (const std::vector<SPItem*> &objects, SPStyle *style_res) { bool different = false; bool set = false; int texts = 0; - for (GSList const *i = objects; i != NULL; i = i->next) { - SPObject *obj = reinterpret_cast<SPObject *>(i->data); + for (std::vector<SPItem*>::const_iterator i = objects.begin(); i != objects.end(); i++) { + SPObject *obj = *i; if (!isTextualItem(obj)) { continue; @@ -1169,11 +1168,156 @@ objects_query_fontstyle (GSList *objects, SPStyle *style_res) } } +int +objects_query_fontvariants (const std::vector<SPItem*> &objects, SPStyle *style_res) +{ + bool set = false; + + int texts = 0; + + SPILigatures* ligatures_res = &(style_res->font_variant_ligatures); + SPIEnum* position_res = &(style_res->font_variant_position); + SPIEnum* caps_res = &(style_res->font_variant_caps); + SPINumeric* numeric_res = &(style_res->font_variant_numeric); + + // Stores 'and' of all values + ligatures_res->computed = SP_CSS_FONT_VARIANT_LIGATURES_NORMAL; + position_res->computed = SP_CSS_FONT_VARIANT_POSITION_NORMAL; + caps_res->computed = SP_CSS_FONT_VARIANT_CAPS_NORMAL; + numeric_res->computed = SP_CSS_FONT_VARIANT_NUMERIC_NORMAL; + + // Stores only differences + ligatures_res->value = 0; + position_res->value = 0; + caps_res->value = 0; + numeric_res->value = 0; + + for (std::vector<SPItem*>::const_iterator i = objects.begin(); i != objects.end(); i++) { + SPObject *obj = *i; + + if (!isTextualItem(obj)) { + continue; + } + + SPStyle *style = obj->style; + if (!style) { + continue; + } + + texts ++; + + SPILigatures* ligatures_in = &(style->font_variant_ligatures); + SPIEnum* position_in = &(style->font_variant_position); + SPIEnum* caps_in = &(style->font_variant_caps); + SPINumeric* numeric_in = &(style->font_variant_numeric); + // computed stores which bits are on/off, only valid if same between all selected objects. + // value stores which bits are different between objects. This is a bit of an abuse of + // the values but then we don't need to add new variables to class. + if (set) { + ligatures_res->value |= (ligatures_res->computed ^ ligatures_in->computed ); + ligatures_res->computed &= ligatures_in->computed; + + position_res->value |= (position_res->computed ^ position_in->computed ); + position_res->computed &= position_in->computed; + + caps_res->value |= (caps_res->computed ^ caps_in->computed ); + caps_res->computed &= caps_in->computed; + + numeric_res->value |= (numeric_res->computed ^ numeric_in->computed ); + numeric_res->computed &= numeric_in->computed; + + } else { + ligatures_res->computed = ligatures_in->computed; + position_res->computed = position_in->computed; + caps_res->computed = caps_in->computed; + numeric_res->computed = numeric_in->computed; + } + + set = true; + } + + bool different = (style_res->font_variant_ligatures.value != 0 || + style_res->font_variant_position.value != 0 || + style_res->font_variant_caps.value != 0 || + style_res->font_variant_numeric.value != 0 ); + + if (texts == 0 || !set) + return QUERY_STYLE_NOTHING; + + if (texts > 1) { + if (different) { + return QUERY_STYLE_MULTIPLE_DIFFERENT; + } else { + return QUERY_STYLE_MULTIPLE_SAME; + } + } else { + return QUERY_STYLE_SINGLE; + } +} + + +int +objects_query_fontfeaturesettings (const std::vector<SPItem*> &objects, SPStyle *style_res) +{ + bool different = false; + int texts = 0; + + if (style_res->font_feature_settings.value) { + g_free(style_res->font_feature_settings.value); + style_res->font_feature_settings.value = NULL; + } + style_res->font_feature_settings.set = FALSE; + + for (std::vector<SPItem*>::const_iterator i = objects.begin(); i != objects.end(); i++) { + SPObject *obj = *i; + + // std::cout << " " << reinterpret_cast<SPObject*>(i->data)->getId() << std::endl; + if (!isTextualItem(obj)) { + continue; + } + + SPStyle *style = obj->style; + if (!style) { + continue; + } + + texts ++; + + if (style_res->font_feature_settings.value && style->font_feature_settings.value && + strcmp (style_res->font_feature_settings.value, style->font_feature_settings.value)) { + different = true; // different fonts + } + + if (style_res->font_feature_settings.value) { + g_free(style_res->font_feature_settings.value); + style_res->font_feature_settings.value = NULL; + } + + style_res->font_feature_settings.set = TRUE; + style_res->font_feature_settings.value = g_strdup(style->font_feature_settings.value); + } + + if (texts == 0 || !style_res->font_feature_settings.set) { + return QUERY_STYLE_NOTHING; + } + + if (texts > 1) { + if (different) { + return QUERY_STYLE_MULTIPLE_DIFFERENT; + } else { + return QUERY_STYLE_MULTIPLE_SAME; + } + } else { + return QUERY_STYLE_SINGLE; + } +} + + /** * Write to style_res the baseline numbers. */ static int -objects_query_baselines (GSList *objects, SPStyle *style_res) +objects_query_baselines (const std::vector<SPItem*> &objects, SPStyle *style_res) { bool different = false; @@ -1192,8 +1336,8 @@ objects_query_baselines (GSList *objects, SPStyle *style_res) int texts = 0; - for (GSList const *i = objects; i != NULL; i = i->next) { - SPObject *obj = reinterpret_cast<SPObject *>(i->data); + for (std::vector<SPItem*>::const_iterator i = objects.begin(); i != objects.end(); i++) { + SPObject *obj = *i; if (!isTextualItem(obj)) { continue; @@ -1269,7 +1413,7 @@ objects_query_baselines (GSList *objects, SPStyle *style_res) * Write to style_res the average font family of objects. */ int -objects_query_fontfamily (GSList *objects, SPStyle *style_res) +objects_query_fontfamily (const std::vector<SPItem*> &objects, SPStyle *style_res) { bool different = false; int texts = 0; @@ -1280,8 +1424,8 @@ objects_query_fontfamily (GSList *objects, SPStyle *style_res) } style_res->font_family.set = FALSE; - for (GSList const *i = objects; i != NULL; i = i->next) { - SPObject *obj = reinterpret_cast<SPObject *>(i->data); + for (std::vector<SPItem*>::const_iterator i = objects.begin(); i != objects.end(); i++) { + SPObject *obj = *i; // std::cout << " " << reinterpret_cast<SPObject*>(i->data)->getId() << std::endl; if (!isTextualItem(obj)) { @@ -1325,7 +1469,7 @@ objects_query_fontfamily (GSList *objects, SPStyle *style_res) } static int -objects_query_fontspecification (GSList *objects, SPStyle *style_res) +objects_query_fontspecification (const std::vector<SPItem*> &objects, SPStyle *style_res) { bool different = false; int texts = 0; @@ -1336,8 +1480,8 @@ objects_query_fontspecification (GSList *objects, SPStyle *style_res) } style_res->font_specification.set = FALSE; - for (GSList const *i = objects; i != NULL; i = i->next) { - SPObject *obj = reinterpret_cast<SPObject *>(i->data); + for (std::vector<SPItem*>::const_iterator i = objects.begin(); i != objects.end(); i++) { + SPObject *obj = *i; // std::cout << " " << reinterpret_cast<SPObject*>(i->data)->getId() << std::endl; if (!isTextualItem(obj)) { @@ -1385,7 +1529,7 @@ objects_query_fontspecification (GSList *objects, SPStyle *style_res) } static int -objects_query_blend (GSList *objects, SPStyle *style_res) +objects_query_blend (const std::vector<SPItem*> &objects, SPStyle *style_res) { const int empty_prev = -2; const int complex_filter = 5; @@ -1394,8 +1538,8 @@ objects_query_blend (GSList *objects, SPStyle *style_res) bool same_blend = true; guint items = 0; - for (GSList const *i = objects; i != NULL; i = i->next) { - SPObject *obj = reinterpret_cast<SPObject *>(i->data); + for (std::vector<SPItem*>::const_iterator i = objects.begin(); i != objects.end(); i++) { + SPObject *obj = *i; if (!obj) { continue; } @@ -1471,9 +1615,9 @@ objects_query_blend (GSList *objects, SPStyle *style_res) * Write to style_res the average blurring of a list of objects. */ int -objects_query_blur (GSList *objects, SPStyle *style_res) +objects_query_blur (const std::vector<SPItem*> &objects, SPStyle *style_res) { - if (g_slist_length(objects) == 0) { + if (objects.empty()) { /* No objects, set empty */ return QUERY_STYLE_NOTHING; } @@ -1484,8 +1628,8 @@ objects_query_blur (GSList *objects, SPStyle *style_res) guint blur_items = 0; guint items = 0; - for (GSList const *i = objects; i != NULL; i = i->next) { - SPObject *obj = reinterpret_cast<SPObject *>(i->data); + for (std::vector<SPItem*>::const_iterator i = objects.begin(); i != objects.end(); i++) { + SPObject *obj = *i; if (!obj) { continue; } @@ -1553,7 +1697,7 @@ objects_query_blur (GSList *objects, SPStyle *style_res) * the result to style, return appropriate flag. */ int -sp_desktop_query_style_from_list (GSList *list, SPStyle *style, int property) +sp_desktop_query_style_from_list (const std::vector<SPItem*> &list, SPStyle *style, int property) { if (property == QUERY_STYLE_PROPERTY_FILL) { return objects_query_fillstroke (list, style, true); @@ -1578,6 +1722,10 @@ sp_desktop_query_style_from_list (GSList *list, SPStyle *style, int property) return objects_query_fontfamily (list, style); } else if (property == QUERY_STYLE_PROPERTY_FONTSTYLE) { return objects_query_fontstyle (list, style); + } else if (property == QUERY_STYLE_PROPERTY_FONTVARIANTS) { + return objects_query_fontvariants (list, style); + } else if (property == QUERY_STYLE_PROPERTY_FONTFEATURESETTINGS) { + return objects_query_fontfeaturesettings (list, style); } else if (property == QUERY_STYLE_PROPERTY_FONTNUMBERS) { return objects_query_fontnumbers (list, style); } else if (property == QUERY_STYLE_PROPERTY_BASELINES) { @@ -1599,6 +1747,7 @@ sp_desktop_query_style_from_list (GSList *list, SPStyle *style, int property) int sp_desktop_query_style(SPDesktop *desktop, SPStyle *style, int property) { + // Used by text tool and in gradient dragging int ret = desktop->_query_style_signal.emit(style, property); if (ret != QUERY_STYLE_NOTHING) @@ -1606,7 +1755,7 @@ sp_desktop_query_style(SPDesktop *desktop, SPStyle *style, int property) // otherwise, do querying and averaging over selection if (desktop->selection != NULL) { - return sp_desktop_query_style_from_list ((GSList *) desktop->selection->itemList(), style, property); + return sp_desktop_query_style_from_list (desktop->selection->itemList(), style, property); } return QUERY_STYLE_NOTHING; diff --git a/src/desktop-style.h b/src/desktop-style.h index 40ca27e9e..95c434844 100644 --- a/src/desktop-style.h +++ b/src/desktop-style.h @@ -17,6 +17,7 @@ class ColorRGBA; class SPCSSAttr; class SPDesktop; class SPObject; +class SPItem; class SPStyle; typedef struct _GSList GSList; namespace Inkscape { @@ -46,6 +47,8 @@ enum { // which property was queried (add when you need more) QUERY_STYLE_PROPERTY_FONT_SPECIFICATION, //-inkscape-font-specification QUERY_STYLE_PROPERTY_FONTFAMILY, // font-family QUERY_STYLE_PROPERTY_FONTSTYLE, // font style + QUERY_STYLE_PROPERTY_FONTVARIANTS, // font variants (OpenType features) + QUERY_STYLE_PROPERTY_FONTFEATURESETTINGS, // font feature settings (OpenType features) QUERY_STYLE_PROPERTY_FONTNUMBERS, // size, spacings QUERY_STYLE_PROPERTY_BASELINES, // baseline-shift QUERY_STYLE_PROPERTY_MASTEROPACITY, // opacity @@ -64,21 +67,22 @@ guint32 sp_desktop_get_color_tool(SPDesktop *desktop, Glib::ustring const &tool, double sp_desktop_get_font_size_tool (SPDesktop *desktop); void sp_desktop_apply_style_tool(SPDesktop *desktop, Inkscape::XML::Node *repr, Glib::ustring const &tool, bool with_text); -gdouble stroke_average_width (GSList const *objects); +gdouble stroke_average_width (const std::vector<SPItem*> &objects); -int objects_query_fillstroke (GSList *objects, SPStyle *style_res, bool const isfill); -int objects_query_fontnumbers (GSList *objects, SPStyle *style_res); -int objects_query_fontstyle (GSList *objects, SPStyle *style_res); -int objects_query_fontfamily (GSList *objects, SPStyle *style_res); -int objects_query_opacity (GSList *objects, SPStyle *style_res); -int objects_query_strokewidth (GSList *objects, SPStyle *style_res); -int objects_query_miterlimit (GSList *objects, SPStyle *style_res); -int objects_query_strokecap (GSList *objects, SPStyle *style_res); -int objects_query_strokejoin (GSList *objects, SPStyle *style_res); +int objects_query_fillstroke (const std::vector<SPItem*> &objects, SPStyle *style_res, bool const isfill); +int objects_query_fontnumbers (const std::vector<SPItem*> &objects, SPStyle *style_res); +int objects_query_fontstyle (const std::vector<SPItem*> &objects, SPStyle *style_res); +int objects_query_fontfamily (const std::vector<SPItem*> &objects, SPStyle *style_res); +int objects_query_fontvariants (const std::vector<SPItem*> &objects, SPStyle *style_res); +int objects_query_opacity (const std::vector<SPItem*> &objects, SPStyle *style_res); +int objects_query_strokewidth (const std::vector<SPItem*> &objects, SPStyle *style_res); +int objects_query_miterlimit (const std::vector<SPItem*> &objects, SPStyle *style_res); +int objects_query_strokecap (const std::vector<SPItem*> &objects, SPStyle *style_res); +int objects_query_strokejoin (const std::vector<SPItem*> &objects, SPStyle *style_res); -int objects_query_blur (GSList *objects, SPStyle *style_res); +int objects_query_blur (const std::vector<SPItem*> &objects, SPStyle *style_res); -int sp_desktop_query_style_from_list (GSList *list, SPStyle *style, int property); +int sp_desktop_query_style_from_list (const std::vector<SPItem*> &list, SPStyle *style, int property); int sp_desktop_query_style(SPDesktop *desktop, SPStyle *style, int property); bool sp_desktop_query_style_all (SPDesktop *desktop, SPStyle *query); diff --git a/src/desktop.cpp b/src/desktop.cpp index 1152184c9..02df50c6b 100644 --- a/src/desktop.cpp +++ b/src/desktop.cpp @@ -716,7 +716,7 @@ Inkscape::UI::Widget::Dock* SPDesktop::getDock() { /** * \see SPDocument::getItemFromListAtPointBottom() */ -SPItem *SPDesktop::getItemFromListAtPointBottom(const GSList *list, Geom::Point const &p) const +SPItem *SPDesktop::getItemFromListAtPointBottom(const std::vector<SPItem*> &list, Geom::Point const &p) const { g_return_val_if_fail (doc() != NULL, NULL); return SPDocument::getItemFromListAtPointBottom(dkey, doc()->getRoot(), list, p); @@ -947,7 +947,6 @@ void SPDesktop::zoom_quick(bool enable) if (!zoomed) { zoom_relative(_quick_zoom_stored_area.midpoint()[Geom::X], _quick_zoom_stored_area.midpoint()[Geom::Y], 2.0); - zoomed = true; } } else { set_display_area(_quick_zoom_stored_area, false); diff --git a/src/desktop.h b/src/desktop.h index a0b9592d0..754e09766 100644 --- a/src/desktop.h +++ b/src/desktop.h @@ -319,7 +319,7 @@ public: //void push_event_context (GType type, const gchar *config, unsigned int key); void set_coordinate_status (Geom::Point p); - SPItem *getItemFromListAtPointBottom(const GSList *list, Geom::Point const &p) const; + SPItem *getItemFromListAtPointBottom(const std::vector<SPItem*> &list, Geom::Point const &p) const; SPItem *getItemAtPoint(Geom::Point const &p, bool into_groups, SPItem *upto = NULL) const; SPItem *getGroupAtPoint(Geom::Point const &p) const; Geom::Point point() const; diff --git a/src/dir-util.cpp b/src/dir-util.cpp index c9b88b007..64f7ab7e7 100644 --- a/src/dir-util.cpp +++ b/src/dir-util.cpp @@ -217,7 +217,7 @@ erange: return (NULL); } -gchar *prepend_current_dir_if_relative(gchar const *uri) +char *prepend_current_dir_if_relative(gchar const *uri) { if (!uri) { return NULL; diff --git a/src/dir-util.h b/src/dir-util.h index e78cad6a6..327e1ad5f 100644 --- a/src/dir-util.h +++ b/src/dir-util.h @@ -12,7 +12,7 @@ #include <cstdlib> #include <string> -/** +/** * Returns a form of \a path relative to \a base if that is easy to construct (eg if \a path * appears to be in the directory specified by \a base), otherwise returns \a path. * @@ -49,7 +49,7 @@ char *inkscape_rel2abs(char const *path, char const *base, char *result, size_t char *inkscape_abs2rel(char const *path, char const *base, char *result, size_t const size); -gchar *prepend_current_dir_if_relative(char const *filename); +char *prepend_current_dir_if_relative(char const *filename); #endif // !SEEN_DIR_UTIL_H diff --git a/src/display/cairo-utils.cpp b/src/display/cairo-utils.cpp index e1f12b04b..59e190676 100644 --- a/src/display/cairo-utils.cpp +++ b/src/display/cairo-utils.cpp @@ -18,9 +18,7 @@ #include <glib/gstdio.h> #include <glibmm/fileutils.h> #include <2geom/pathvector.h> -#include <2geom/bezier-curve.h> -#include <2geom/elliptical-arc.h> -#include <2geom/hvlinesegment.h> +#include <2geom/curves.h> #include <2geom/affine.h> #include <2geom/point.h> #include <2geom/path.h> @@ -531,7 +529,7 @@ feed_curve_to_cairo(cairo_t *cr, Geom::Curve const &c, Geom::Affine const & tran case 2: { Geom::QuadraticBezier const *quadratic_bezier = static_cast<Geom::QuadraticBezier const*>(&c); - std::vector<Geom::Point> points = quadratic_bezier->points(); + std::vector<Geom::Point> points = quadratic_bezier->controlPoints(); points[0] *= trans; points[1] *= trans; points[2] *= trans; @@ -554,7 +552,7 @@ feed_curve_to_cairo(cairo_t *cr, Geom::Curve const &c, Geom::Affine const & tran case 3: { Geom::CubicBezier const *cubic_bezier = static_cast<Geom::CubicBezier const*>(&c); - std::vector<Geom::Point> points = cubic_bezier->points(); + std::vector<Geom::Point> points = cubic_bezier->controlPoints(); //points[0] *= trans; // don't do this one here for fun: it is only needed for optimized strokes points[1] *= trans; points[2] *= trans; diff --git a/src/display/canvas-arena.cpp b/src/display/canvas-arena.cpp index 8738b93e4..ec99eca9a 100644 --- a/src/display/canvas-arena.cpp +++ b/src/display/canvas-arena.cpp @@ -290,7 +290,7 @@ sp_canvas_arena_event (SPCanvasItem *item, GdkEvent *event) ret = sp_canvas_arena_send_event (arena, (GdkEvent *) &ec); } } - ret = sp_canvas_arena_send_event (arena, event); + ret = ret || sp_canvas_arena_send_event (arena, event); break; case GDK_SCROLL: { diff --git a/src/display/canvas-axonomgrid.cpp b/src/display/canvas-axonomgrid.cpp index a2df86823..1794ccbab 100644 --- a/src/display/canvas-axonomgrid.cpp +++ b/src/display/canvas-axonomgrid.cpp @@ -17,10 +17,6 @@ # include "config.h" #endif -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include <gtkmm/box.h> #include <gtkmm/label.h> diff --git a/src/display/canvas-grid.cpp b/src/display/canvas-grid.cpp index 9b9f64529..decf93626 100644 --- a/src/display/canvas-grid.cpp +++ b/src/display/canvas-grid.cpp @@ -17,10 +17,6 @@ # include "config.h" #endif -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include <gtkmm/box.h> #include <gtkmm/label.h> @@ -910,7 +906,7 @@ CanvasXYGrid::Render (SPCanvasBuf *buf) gdouble const syg = floor ((buf->rect.top() - ow[Geom::Y]) / sw[Geom::Y]) * sw[Geom::Y] + ow[Geom::Y]; gint const ylinestart = round((syg - ow[Geom::Y]) / sw[Geom::Y]); - //set correct coloring, depending preference (when zoomed out, always major coloring or minor coloring) + // no_emphasize_when_zoomedout determines color (minor or major) when only major grid lines/dots shown. Inkscape::Preferences *prefs = Inkscape::Preferences::get(); guint32 _empcolor; bool no_emp_when_zoomed_out = prefs->getBool("/options/grids/no_emphasize_when_zoomedout", false); @@ -926,6 +922,7 @@ CanvasXYGrid::Render (SPCanvasBuf *buf) cairo_set_line_cap(buf->ct, CAIRO_LINE_CAP_SQUARE); if (!render_dotted) { + // Line grid gint ylinenum; gdouble y; for (y = syg, ylinenum = ylinestart; y < buf->rect.bottom(); y += sw[Geom::Y], ylinenum++) { @@ -948,8 +945,23 @@ CanvasXYGrid::Render (SPCanvasBuf *buf) } } } else { + // Dotted grid gint ylinenum; gdouble y; + + // alpha needs to be larger than in the line case to maintain a similar visual impact but + // setting it to the maximal value makes the dots dominant in some cases. Solution, + // increase the alpha by a factor of 4. This then allows some user adjustment. + guint32 _empdot = (_empcolor & 0xff) << 2; + if (_empdot > 0xff) + _empdot = 0xff; + _empdot += (_empcolor & 0xffffff00); + + guint32 _colordot = (color & 0xff) << 2; + if (_colordot > 0xff) + _colordot = 0xff; + _colordot += (color & 0xffffff00); + for (y = syg, ylinenum = ylinestart; y < buf->rect.bottom(); y += sw[Geom::Y], ylinenum++) { gint const iy = round(y); @@ -961,13 +973,15 @@ CanvasXYGrid::Render (SPCanvasBuf *buf) || (!scaled[Geom::Y] && (ylinenum % empspacing) != 0) || ((scaled[Geom::X] || scaled[Geom::Y]) && no_emp_when_zoomed_out) ) { - grid_dot (buf, ix, iy, color | (guint32)0x000000FF); // put alpha to max value + // Minor point: dot only + grid_dot (buf, ix, iy, _colordot); // | (guint32)0x000000FF); // put alpha to max value } else { + // Major point: small cross gint const pitch = 1; grid_dot (buf, ix-pitch, iy, _empcolor); grid_dot (buf, ix+pitch, iy, _empcolor); - grid_dot (buf, ix, iy, _empcolor | (guint32)0x000000FF); // put alpha to max value + grid_dot (buf, ix, iy, _empdot ); // | (guint32)0x000000FF); // put alpha to max value grid_dot (buf, ix, iy-pitch, _empcolor); grid_dot (buf, ix, iy+pitch, _empcolor); diff --git a/src/display/curve-test.h b/src/display/curve-test.h index 3d698ca07..f921a5361 100644 --- a/src/display/curve-test.h +++ b/src/display/curve-test.h @@ -16,17 +16,18 @@ public: CurveTest() : path4(Geom::Point(3,5)) // Just a moveto { // Closed path - path1.append(Geom::HLineSegment(Geom::Point(0,0),1)); - path1.append(Geom::VLineSegment(Geom::Point(1,0),1)); + path1.append(Geom::LineSegment(Geom::Point(0,0),Geom::Point(1,0))); + path1.append(Geom::LineSegment(Geom::Point(1,0),Geom::Point(1,1))); path1.close(); // Closed path (ClosingSegment is zero length) path2.append(Geom::LineSegment(Geom::Point(2,0),Geom::Point(3,0))); - // TODO fix path2.append(Geom::BezierCurve<3>(Geom::Point(3,0),Geom::Point(2,1),Geom::Point(1,1),Geom::Point(2,0))); + path2.append(Geom::CubicBezier(Geom::Point(3,0),Geom::Point(2,1),Geom::Point(1,1),Geom::Point(2,0))); path2.close(); // Open path - path3.append(Geom::SVGEllipticalArc(Geom::Point(4,0),1,2,M_PI,false,false,Geom::Point(5,1))); - path3.append(Geom::VLineSegment(Geom::Point(5,1),2), Geom::Path::STITCH_DISCONTINUOUS); - path3.append(Geom::HLineSegment(Geom::Point(6,4),2), Geom::Path::STITCH_DISCONTINUOUS); + path3.setStitching(true); + path3.append(Geom::EllipticalArc(Geom::Point(4,0),1,2,M_PI,false,false,Geom::Point(5,1))); + path3.append(Geom::LineSegment(Geom::Point(5,1),Geom::Point(5,2))); + path3.append(Geom::LineSegment(Geom::Point(6,4),Geom::Point(2,4))); } virtual ~CurveTest() {} @@ -49,17 +50,17 @@ public: TS_ASSERT_EQUALS(curve.get_segment_count() , 0u); } { // Individual paths - Geom::PathVector pv(1, Geom::Path()); + Geom::PathVector pv((Geom::Path())); pv[0] = path1; TS_ASSERT_EQUALS(SPCurve(pv).get_segment_count() , 3u); pv[0] = path2; - TS_ASSERT_EQUALS(SPCurve(pv).get_segment_count() , 3u); + TS_ASSERT_EQUALS(SPCurve(pv).get_segment_count() , 2u); pv[0] = path3; TS_ASSERT_EQUALS(SPCurve(pv).get_segment_count() , 4u); pv[0] = path4; TS_ASSERT_EQUALS(SPCurve(pv).get_segment_count() , 0u); pv[0].close(); - TS_ASSERT_EQUALS(SPCurve(pv).get_segment_count() , 1u); + TS_ASSERT_EQUALS(SPCurve(pv).get_segment_count() , 0u); } { // Combination Geom::PathVector pv; @@ -68,7 +69,7 @@ public: pv.push_back(path3); pv.push_back(path4); SPCurve curve(pv); - TS_ASSERT_EQUALS(curve.get_segment_count() , 10u); + TS_ASSERT_EQUALS(curve.get_segment_count() , 9u); } } @@ -86,7 +87,7 @@ public: TS_ASSERT_EQUALS(curve.nodes_in_path() , 1u); } { // Individual paths - Geom::PathVector pv(1, Geom::Path()); + Geom::PathVector pv((Geom::Path())); pv[0] = path1; TS_ASSERT_EQUALS(SPCurve(pv).nodes_in_path() , 3u); pv[0] = path2; @@ -112,28 +113,28 @@ public: void testIsEmpty() { TS_ASSERT(SPCurve(Geom::PathVector()).is_empty()); - TS_ASSERT(!SPCurve(Geom::PathVector(1, path1)).is_empty()); - TS_ASSERT(!SPCurve(Geom::PathVector(1, path2)).is_empty()); - TS_ASSERT(!SPCurve(Geom::PathVector(1, path3)).is_empty()); - TS_ASSERT(!SPCurve(Geom::PathVector(1, path4)).is_empty()); + TS_ASSERT(!SPCurve(path1).is_empty()); + TS_ASSERT(!SPCurve(path2).is_empty()); + TS_ASSERT(!SPCurve(path3).is_empty()); + TS_ASSERT(!SPCurve(path4).is_empty()); } void testIsClosed() { TS_ASSERT(!SPCurve(Geom::PathVector()).is_closed()); - Geom::PathVector pv(1, Geom::Path()); + Geom::PathVector pv((Geom::Path())); TS_ASSERT(!SPCurve(pv).is_closed()); pv[0].close(); TS_ASSERT(SPCurve(pv).is_closed()); - TS_ASSERT(SPCurve(Geom::PathVector(1, path1)).is_closed()); - TS_ASSERT(SPCurve(Geom::PathVector(1, path2)).is_closed()); - TS_ASSERT(!SPCurve(Geom::PathVector(1, path3)).is_closed()); - TS_ASSERT(!SPCurve(Geom::PathVector(1, path4)).is_closed()); + TS_ASSERT(SPCurve(path1).is_closed()); + TS_ASSERT(SPCurve(path2).is_closed()); + TS_ASSERT(!SPCurve(path3).is_closed()); + TS_ASSERT(!SPCurve(path4).is_closed()); } void testLastFirstSegment() { - Geom::PathVector pv(1, path4); + Geom::PathVector pv(path4); TS_ASSERT_EQUALS(SPCurve(pv).first_segment() , (void*)0); TS_ASSERT_EQUALS(SPCurve(pv).last_segment() , (void*)0); pv[0].close(); @@ -185,10 +186,10 @@ public: void testFirstPoint() { - TS_ASSERT_EQUALS(*(SPCurve(Geom::PathVector(1, path1)).first_point()) , Geom::Point(0,0)); - TS_ASSERT_EQUALS(*(SPCurve(Geom::PathVector(1, path2)).first_point()) , Geom::Point(2,0)); - TS_ASSERT_EQUALS(*(SPCurve(Geom::PathVector(1, path3)).first_point()) , Geom::Point(4,0)); - TS_ASSERT_EQUALS(*(SPCurve(Geom::PathVector(1, path4)).first_point()) , Geom::Point(3,5)); + TS_ASSERT_EQUALS(*(SPCurve(path1).first_point()) , Geom::Point(0,0)); + TS_ASSERT_EQUALS(*(SPCurve(path2).first_point()) , Geom::Point(2,0)); + TS_ASSERT_EQUALS(*(SPCurve(path3).first_point()) , Geom::Point(4,0)); + TS_ASSERT_EQUALS(*(SPCurve(path4).first_point()) , Geom::Point(3,5)); Geom::PathVector pv; TS_ASSERT(!SPCurve(pv).first_point()); pv.push_back(path1); @@ -201,10 +202,10 @@ public: void testLastPoint() { - TS_ASSERT_EQUALS(*(SPCurve(Geom::PathVector(1, path1)).last_point()) , Geom::Point(0,0)); - TS_ASSERT_EQUALS(*(SPCurve(Geom::PathVector(1, path2)).last_point()) , Geom::Point(2,0)); - TS_ASSERT_EQUALS(*(SPCurve(Geom::PathVector(1, path3)).last_point()) , Geom::Point(8,4)); - TS_ASSERT_EQUALS(*(SPCurve(Geom::PathVector(1, path4)).last_point()) , Geom::Point(3,5)); + TS_ASSERT_EQUALS(*(SPCurve(path1).last_point()) , Geom::Point(0,0)); + TS_ASSERT_EQUALS(*(SPCurve(path2).last_point()) , Geom::Point(2,0)); + TS_ASSERT_EQUALS(*(SPCurve(path3).last_point()) , Geom::Point(8,4)); + TS_ASSERT_EQUALS(*(SPCurve(path4).last_point()) , Geom::Point(3,5)); Geom::PathVector pv; TS_ASSERT(!SPCurve(pv).last_point()); pv.push_back(path1); @@ -217,10 +218,10 @@ public: void testSecondPoint() { - TS_ASSERT_EQUALS( *(SPCurve(Geom::PathVector(1, path1)).second_point()) , Geom::Point(1,0)); - TS_ASSERT_EQUALS( *(SPCurve(Geom::PathVector(1, path2)).second_point()) , Geom::Point(3,0)); - TS_ASSERT_EQUALS( *(SPCurve(Geom::PathVector(1, path3)).second_point()) , Geom::Point(5,1)); - TS_ASSERT_EQUALS( *(SPCurve(Geom::PathVector(1, path4)).second_point()) , Geom::Point(3,5)); + TS_ASSERT_EQUALS( *(SPCurve(path1).second_point()) , Geom::Point(1,0)); + TS_ASSERT_EQUALS( *(SPCurve(path2).second_point()) , Geom::Point(3,0)); + TS_ASSERT_EQUALS( *(SPCurve(path3).second_point()) , Geom::Point(5,1)); + TS_ASSERT_EQUALS( *(SPCurve(path4).second_point()) , Geom::Point(3,5)); Geom::PathVector pv; pv.push_back(path1); pv.push_back(path2); @@ -232,10 +233,10 @@ public: void testPenultimatePoint() { - TS_ASSERT_EQUALS( *(SPCurve(Geom::PathVector(1, path1)).penultimate_point()) , Geom::Point(1,1)); - TS_ASSERT_EQUALS( *(SPCurve(Geom::PathVector(1, path2)).penultimate_point()) , Geom::Point(3,0)); - TS_ASSERT_EQUALS( *(SPCurve(Geom::PathVector(1, path3)).penultimate_point()) , Geom::Point(6,4)); - TS_ASSERT_EQUALS( *(SPCurve(Geom::PathVector(1, path4)).penultimate_point()) , Geom::Point(3,5)); + TS_ASSERT_EQUALS( *(SPCurve(Geom::PathVector(path1)).penultimate_point()) , Geom::Point(1,1)); + TS_ASSERT_EQUALS( *(SPCurve(Geom::PathVector(path2)).penultimate_point()) , Geom::Point(3,0)); + TS_ASSERT_EQUALS( *(SPCurve(Geom::PathVector(path3)).penultimate_point()) , Geom::Point(6,4)); + TS_ASSERT_EQUALS( *(SPCurve(Geom::PathVector(path4)).penultimate_point()) , Geom::Point(3,5)); Geom::PathVector pv; pv.push_back(path1); pv.push_back(path2); diff --git a/src/display/curve.cpp b/src/display/curve.cpp index 54a62939d..3024d1276 100644 --- a/src/display/curve.cpp +++ b/src/display/curve.cpp @@ -31,14 +31,12 @@ SPCurve::SPCurve() : _refcount(1), _pathv() -{ -} +{} SPCurve::SPCurve(Geom::PathVector const& pathv) : _refcount(1), _pathv(pathv) -{ -} +{} SPCurve * SPCurve::new_from_rect(Geom::Rect const &rect, bool all_four_sides) @@ -90,13 +88,7 @@ SPCurve::get_pathvector() const size_t SPCurve::get_segment_count() const { - size_t nr = 0; - for(Geom::PathVector::const_iterator it = _pathv.begin(); it != _pathv.end(); ++it) { - nr += (*it).size(); - - if (it->closed()) nr += 1; - } - return nr; + return _pathv.curveCount(); } /** @@ -211,8 +203,9 @@ SPCurve::moveto(double x, double y) void SPCurve::moveto(Geom::Point const &p) { - _pathv.push_back( Geom::Path() ); // for some reason Geom::Path(p) does not work... - _pathv.back().start(p); + Geom::Path path(p); + path.setStitching(true); + _pathv.push_back(path); } /** @@ -476,7 +469,7 @@ SPCurve::last_point() const SPCurve * SPCurve::create_reverse() const { - SPCurve *new_curve = new SPCurve(Geom::reverse_paths_and_order(_pathv)); + SPCurve *new_curve = new SPCurve(_pathv.reversed()); return new_curve; } @@ -635,20 +628,10 @@ SPCurve::nodes_in_path() const { size_t nr = 0; for(Geom::PathVector::const_iterator it = _pathv.begin(); it != _pathv.end(); ++it) { - nr += (*it).size(); - - nr++; // count last node (this works also for closed paths because although they don't have a 'last node', they do have an extra segment - - // do not count closing knot double for zero-length closing line segments - // however, if the path is only a moveto, and is closed, do not subtract 1 (otherwise the result will be zero nodes) - if ( it->closed() - && ((*it).size() != 0) ) - { - Geom::Curve const &c = it->back_closed(); - if (are_near(c.initialPoint(), c.finalPoint())) { - nr--; - } - } + // if the path does not have any segments, it is a naked moveto, + // and therefore any path has at least one valid node + size_t psize = std::max<size_t>(1, it->size_closed()); + nr += psize; } return nr; diff --git a/src/display/curve.h b/src/display/curve.h index 5fad75b18..42b899210 100644 --- a/src/display/curve.h +++ b/src/display/curve.h @@ -13,7 +13,7 @@ #ifndef SEEN_DISPLAY_CURVE_H #define SEEN_DISPLAY_CURVE_H -#include <2geom/forward.h> +#include <2geom/pathvector.h> #include <cstddef> #include <boost/optional.hpp> diff --git a/src/display/drawing-image.cpp b/src/display/drawing-image.cpp index 8fe337959..1594614ac 100644 --- a/src/display/drawing-image.cpp +++ b/src/display/drawing-image.cpp @@ -172,7 +172,7 @@ static double distance_to_segment (Geom::Point const &p, Geom::Point const &a1, Geom::Point const &a2) { Geom::LineSegment l(a1, a2); - Geom::Point np = l.pointAt(l.nearestPoint(p)); + Geom::Point np = l.pointAt(l.nearestTime(p)); return Geom::distance(np, p); } diff --git a/src/display/drawing-text.cpp b/src/display/drawing-text.cpp index e20a7ff2a..a3ca7173a 100644 --- a/src/display/drawing-text.cpp +++ b/src/display/drawing-text.cpp @@ -91,9 +91,24 @@ unsigned DrawingGlyphs::_updateItem(Geom::IntRect const &/*area*/, UpdateContext if (_transform) { scale_bigbox /= _transform->descrim(); } + - Geom::Rect bigbox(Geom::Point(-_width*scale_bigbox*0.1, _asc*scale_bigbox*1.1),Geom::Point(_width*scale_bigbox, -_dsc*scale_bigbox*1.1)); - Geom::Rect b = bigbox * ctx.ctm; + /* Because there can be text decorations the bounding box must correspond in Y to a little above the glyph's ascend + and a little below its descend. This leaves room for overline and underline. The left and right sides + come from the glyph's bounding box. Note that the initial direction of ascender is positive down in Y, and + this flips after the transform is applied. So change the sign on descender. 1.1 provides a little extra space + above and below the max/min y positions of the letters to place the text decorations.*/ + + Geom::Rect b; + if(_drawable){ + Geom::OptRect tiltb = bounds_exact(*_font->PathVector(_glyph)); + Geom::Rect bigbox(Geom::Point(tiltb->left(),-_dsc*scale_bigbox*1.1),Geom::Point(tiltb->right(),_asc*scale_bigbox*1.1)); + b = bigbox * ctx.ctm; + } + else { // Fallback, spaces mostly + Geom::Rect bigbox(Geom::Point(0.0, -_dsc*scale_bigbox*1.1),Geom::Point(_width*scale_bigbox, _asc*scale_bigbox*1.1)); + b = bigbox * ctx.ctm; + } /* The pick box matches the characters as best as it can, leaving no extra space above or below @@ -108,11 +123,11 @@ unsigned DrawingGlyphs::_updateItem(Geom::IntRect const &/*area*/, UpdateContext if(_drawable){ pb = bounds_exact_transformed(*_font->PathVector(_glyph), ctx.ctm); } - if(!pb){ // Fallback + if(!pb){ // Fallback, spaces mostly Geom::Rect pbigbox(Geom::Point(0.0, _asc*scale_bigbox*0.66),Geom::Point(_width*scale_bigbox, 0.0)); pb = pbigbox * ctx.ctm; } - + #if 0 /* FIXME if this is commented out then not even an approximation of pick on decorations */ /* adjust the pick box up or down to include the decorations. @@ -214,7 +229,7 @@ DrawingText::addComponent(font_instance *font, int glyph, Geom::Affine const &tr ng->setGlyph(font, glyph, trans); if(font->PathVector(glyph)){ ng->_drawable = true; } else { ng->_drawable = false; } - ng->_width = width; // only used when _drawable = false + ng->_width = width; // used especially when _drawable = false, otherwise, it is the advance of the font ng->_asc = ascent; // of font, not of this one character ng->_dsc = descent; // of font, not of this one character ng->_pl = phase_length; // used for phase of dots, dashes, and wavy @@ -367,7 +382,7 @@ void DrawingText::decorateStyle(DrawingContext &dc, double vextent, double xphas /* returns scaled line thickness */ void DrawingText::decorateItem(DrawingContext &dc, double phase_length, bool under) { - if (_nrstyle.font_size < 1.0e-32)return; // would cause a divide by zero and nothing would be visible anyway + if ( _nrstyle.font_size <= 1.0e-32 )return; // might cause a divide by zero or overflow and nothing would be visible anyway double tsp_width_adj = _nrstyle.tspan_width / _nrstyle.font_size; double tsp_asc_adj = _nrstyle.ascender / _nrstyle.font_size; double tsp_size_adj = (_nrstyle.ascender + _nrstyle.descender) / _nrstyle.font_size; @@ -381,6 +396,7 @@ void DrawingText::decorateItem(DrawingContext &dc, double phase_length, bool und Geom::Point p2; // All lines must be the same thickness, in combinations, line_through trumps underline double thickness = final_underline_thickness; + if ( thickness <= 1.0e-32 )return; // might cause a divide by zero or overflow and nothing would be visible anyway dc.setTolerance(0.5); // Is this really necessary... could effect dots. if( under ) { diff --git a/src/display/snap-indicator.cpp b/src/display/snap-indicator.cpp index 8c0c8163f..926b35599 100644 --- a/src/display/snap-indicator.cpp +++ b/src/display/snap-indicator.cpp @@ -258,6 +258,19 @@ SnapIndicator::set_new_snaptarget(Inkscape::SnappedPoint const &p, bool pre_snap const int timeout_val = 4000; + // The snap indicator will be deleted after some time-out, and sp_canvas_item_dispose + // will be called. This will set canvas->current_item to NULL if the snap indicator was + // the current item, after which any events will go to the root handler instead of any + // item handler. Dragging an object which has just snapped might therefore not be possible + // without selecting / repicking it again. To avoid this, we make sure here that the + // snap indicator will never be picked, and will therefore never be the current item. + // Reported bugs: + // - scrolling when hovering above a pre-snap indicator won't work (for example) + // (https://bugs.launchpad.net/inkscape/+bug/522335/comments/8) + // - dragging doesn't work without repicking + // (https://bugs.launchpad.net/inkscape/+bug/1420301/comments/15) + SP_CTRL(canvasitem)->pickable = false; + SP_CTRL(canvasitem)->moveto(p.getPoint()); _snaptarget = _desktop->add_temporary_canvasitem(canvasitem, timeout_val); _snaptarget_is_presnap = pre_snap; @@ -282,6 +295,7 @@ SnapIndicator::set_new_snaptarget(Inkscape::SnappedPoint const &p, bool pre_snap SPCanvasItem *canvas_tooltip = sp_canvastext_new(_desktop->getTempGroup(), _desktop, tooltip_pos, tooltip_str); sp_canvastext_set_fontsize(SP_CANVASTEXT(canvas_tooltip), fontsize); + SP_CANVASTEXT(canvas_tooltip)->pickable = false; // See the extensive comment above SP_CANVASTEXT(canvas_tooltip)->rgba = 0xffffffff; SP_CANVASTEXT(canvas_tooltip)->outline = false; SP_CANVASTEXT(canvas_tooltip)->background = true; @@ -306,6 +320,7 @@ SnapIndicator::set_new_snaptarget(Inkscape::SnappedPoint const &p, bool pre_snap SP_CTRLRECT(box)->setRectangle(*bbox); SP_CTRLRECT(box)->setColor(pre_snap ? 0x7f7f7fff : 0xff0000ff, 0, 0); SP_CTRLRECT(box)->setDashed(true); + SP_CTRLRECT(box)->pickable = false; // See the extensive comment above sp_canvas_item_move_to_z(box, 0); _snaptarget_bbox = _desktop->add_temporary_canvasitem(box, timeout_val); } diff --git a/src/display/sp-canvas-item.h b/src/display/sp-canvas-item.h index 3b7b7bd4f..3e9e085a0 100644 --- a/src/display/sp-canvas-item.h +++ b/src/display/sp-canvas-item.h @@ -69,7 +69,12 @@ struct SPCanvasItem { gboolean visible; gboolean need_update; gboolean need_affine; - + + // If true, then SPCanvasGroup::point() and sp_canvas_item_invoke_point() will calculate + // the distance to the pointer, such that this item can be picked in pickCurrentItem() + // Only if an item can be picked, then it can be set as current_item and receive events! + bool pickable; + bool in_destruction; }; diff --git a/src/display/sp-canvas.cpp b/src/display/sp-canvas.cpp index 305b0950a..5efc4ce86 100644 --- a/src/display/sp-canvas.cpp +++ b/src/display/sp-canvas.cpp @@ -375,6 +375,7 @@ sp_canvas_item_init(SPCanvasItem *item) // used for rubberbanding, path outline, etc. item->visible = TRUE; item->in_destruction = false; + item->pickable = true; } SPCanvasItem *sp_canvas_item_new(SPCanvasGroup *parent, GType type, gchar const *first_arg_name, ...) @@ -728,7 +729,6 @@ bool sp_canvas_item_is_visible(SPCanvasItem *item) return item->visible; } - /** * Sets visible flag on item and requests a redraw. */ @@ -1009,21 +1009,21 @@ double SPCanvasGroup::point(SPCanvasItem *item, Geom::Point p, SPCanvasItem **ac if ((child->x1 <= x2) && (child->y1 <= y2) && (child->x2 >= x1) && (child->y2 >= y1)) { SPCanvasItem *point_item = NULL; // cater for incomplete item implementations - int has_point; - if (child->visible && SP_CANVAS_ITEM_GET_CLASS(child)->point) { + int pickable; + if (child->visible && child->pickable && SP_CANVAS_ITEM_GET_CLASS(child)->point) { dist = sp_canvas_item_invoke_point(child, p, &point_item); - has_point = TRUE; + pickable = TRUE; } else { - has_point = FALSE; + pickable = FALSE; } - // This metric should be improved, because in case of (partly) overlapping items we will now + // TODO: This metric should be improved, because in case of (partly) overlapping items we will now // always select the last one that has been added to the group. We could instead select the one // of which the center is the closest, for example. One can then move to the center // of the item to be focused, and have that one selected. Of course this will only work if the // centers are not coincident, but at least it's better than what we have now. // See the extensive comment in Inkscape::SelTrans::_updateHandles() - if (has_point && point_item && ((int) (dist + 0.5) <= item->canvas->close_enough)) { + if (pickable && point_item && ((int) (dist + 0.5) <= item->canvas->close_enough)) { best = dist; *actual_item = point_item; } @@ -1480,20 +1480,6 @@ int SPCanvasImpl::emitEvent(SPCanvas *canvas, GdkEvent *event) if (canvas->grabbed_item && !is_descendant (canvas->current_item, canvas->grabbed_item)) { item = canvas->grabbed_item; } else { - // Make sure that current_item is up-to-date. If a snap indicator was just deleted, then - // sp_canvas_item_dispose has been called and there is no current_item specified. We need - // that though because otherwise we don't know where to send this event to, leading to a - // lost event. We can't wait for idle events to have current_item updated, we need it now! - // Otherwise, scrolling when hovering above a pre-snap indicator won't work (for example) - // See this bug report: https://bugs.launchpad.net/inkscape/+bug/522335/comments/8 - if (canvas->need_repick && !canvas->in_repick && event->type == GDK_SCROLL) { - // To avoid side effects, we'll only do this for scroll events, because this is the - // only thing we want to fix here. An example of a reported side effect is that - // otherwise selection of nodes in the node editor by dragging a rectangle using a - // tablet will break - canvas->need_repick = FALSE; - pickCurrentItem(canvas, reinterpret_cast<GdkEvent *>(event)); - } item = canvas->current_item; } @@ -1527,7 +1513,7 @@ int SPCanvasImpl::pickCurrentItem(SPCanvas *canvas, GdkEvent *event) { int button_down = 0; - if (!canvas->root) // canvas may have already be destroyed by closing desktop durring interrupted display! + if (!canvas->root) // canvas may have already be destroyed by closing desktop during interrupted display! return FALSE; int retval = FALSE; diff --git a/src/display/sp-canvas.h b/src/display/sp-canvas.h index 48c3de2fc..65b06ade8 100644 --- a/src/display/sp-canvas.h +++ b/src/display/sp-canvas.h @@ -23,6 +23,7 @@ #endif #include <gtk/gtk.h> +#include <stdint.h> #include <glibmm/ustring.h> #include <2geom/affine.h> #include <2geom/rect.h> diff --git a/src/document-undo.cpp b/src/document-undo.cpp index d4015bafb..59e060cd5 100644 --- a/src/document-undo.cpp +++ b/src/document-undo.cpp @@ -217,6 +217,25 @@ static void finish_incomplete_transaction(SPDocument &doc) { } } +static void perform_document_update(SPDocument &doc) { + sp_repr_begin_transaction(doc.rdoc); + doc.ensureUpToDate(); + + Inkscape::XML::Event *update_log=sp_repr_commit_undoable(doc.rdoc); + if (update_log != NULL) { + g_warning("Document was modified while being updated after undo operation"); + sp_repr_debug_print_log(update_log); + + //Coalesce the update changes with the last action performed by user + Inkscape::Event* undo_stack_top = (Inkscape::Event *)doc.priv->undo->data; + if (undo_stack_top) { + undo_stack_top->event = sp_repr_coalesce_log(undo_stack_top->event, update_log); + } else { + sp_repr_free_log(update_log); + } + } +} + gboolean Inkscape::DocumentUndo::undo(SPDocument *doc) { using Inkscape::Debug::EventTracker; @@ -241,7 +260,8 @@ gboolean Inkscape::DocumentUndo::undo(SPDocument *doc) Inkscape::Event *log=(Inkscape::Event *)doc->priv->undo->data; doc->priv->undo = g_slist_remove (doc->priv->undo, log); sp_repr_undo_log (log->event); - //doc->_updateDocument(); + perform_document_update(*doc); + doc->priv->redo = g_slist_prepend (doc->priv->redo, log); doc->setModifiedSinceSave(); diff --git a/src/document.cpp b/src/document.cpp index 1c6bb76de..ebf5d312f 100644 --- a/src/document.cpp +++ b/src/document.cpp @@ -1266,7 +1266,7 @@ static bool overlaps(Geom::Rect const &area, Geom::Rect const &box) return area.intersects(box); } -static GSList *find_items_in_area(GSList *s, SPGroup *group, unsigned int dkey, Geom::Rect const &area, +static std::vector<SPItem*> &find_items_in_area(std::vector<SPItem*> &s, SPGroup *group, unsigned int dkey, Geom::Rect const &area, bool (*test)(Geom::Rect const &, Geom::Rect const &), bool take_insensitive = false) { g_return_val_if_fail(SP_IS_GROUP(group), s); @@ -1279,7 +1279,7 @@ static GSList *find_items_in_area(GSList *s, SPGroup *group, unsigned int dkey, SPItem *child = SP_ITEM(o); Geom::OptRect box = child->desktopVisualBounds(); if ( box && test(area, *box) && (take_insensitive || child->isVisibleAndUnlocked(dkey))) { - s = g_slist_append(s, child); + s.push_back(child); } } } @@ -1306,7 +1306,7 @@ static bool item_is_in_group(SPItem *item, SPGroup *group) return inGroup; } -SPItem *SPDocument::getItemFromListAtPointBottom(unsigned int dkey, SPGroup *group, GSList const *list,Geom::Point const &p, bool take_insensitive) +SPItem *SPDocument::getItemFromListAtPointBottom(unsigned int dkey, SPGroup *group, std::vector<SPItem*> const &list,Geom::Point const &p, bool take_insensitive) { g_return_val_if_fail(group, NULL); SPItem *bottomMost = 0; @@ -1320,7 +1320,7 @@ SPItem *SPDocument::getItemFromListAtPointBottom(unsigned int dkey, SPGroup *gro Inkscape::DrawingItem *arenaitem = item->get_arenaitem(dkey); if (arenaitem && arenaitem->pick(p, delta, 1) != NULL && (take_insensitive || item->isVisibleAndUnlocked(dkey))) { - if (g_slist_find((GSList *) list, item) != NULL) { + if (find(list.begin(),list.end(),item)!=list.end() ) { bottomMost = item; } } @@ -1422,11 +1422,11 @@ static SPItem *find_group_at_point(unsigned int dkey, SPGroup *group, Geom::Poin * Assumes box is normalized (and g_asserts it!) * */ -GSList *SPDocument::getItemsInBox(unsigned int dkey, Geom::Rect const &box) const +std::vector<SPItem*> SPDocument::getItemsInBox(unsigned int dkey, Geom::Rect const &box) const { - g_return_val_if_fail(this->priv != NULL, NULL); - - return find_items_in_area(NULL, SP_GROUP(this->root), dkey, box, is_within); + std::vector<SPItem*> x; + g_return_val_if_fail(this->priv != NULL, x); + return find_items_in_area(x, SP_GROUP(this->root), dkey, box, is_within); } /* @@ -1436,16 +1436,16 @@ GSList *SPDocument::getItemsInBox(unsigned int dkey, Geom::Rect const &box) cons * */ -GSList *SPDocument::getItemsPartiallyInBox(unsigned int dkey, Geom::Rect const &box) const +std::vector<SPItem*> SPDocument::getItemsPartiallyInBox(unsigned int dkey, Geom::Rect const &box) const { - g_return_val_if_fail(this->priv != NULL, NULL); - - return find_items_in_area(NULL, SP_GROUP(this->root), dkey, box, overlaps); + std::vector<SPItem*> x; + g_return_val_if_fail(this->priv != NULL, x); + return find_items_in_area(x, SP_GROUP(this->root), dkey, box, overlaps); } -GSList *SPDocument::getItemsAtPoints(unsigned const key, std::vector<Geom::Point> points) const +std::vector<SPItem*> SPDocument::getItemsAtPoints(unsigned const key, std::vector<Geom::Point> points) const { - GSList *items = NULL; + std::vector<SPItem*> items; Inkscape::Preferences *prefs = Inkscape::Preferences::get(); // When picking along the path, we don't want small objects close together @@ -1454,11 +1454,11 @@ GSList *SPDocument::getItemsAtPoints(unsigned const key, std::vector<Geom::Point gdouble saved_delta = prefs->getDouble("/options/cursortolerance/value", 1.0); prefs->setDouble("/options/cursortolerance/value", 0.25); - for(unsigned int i = 0; i < points.size(); i++) { + for(int i = points.size()-1;i>=0; i--) { SPItem *item = getItemAtPoint(key, points[i], false, NULL); - if (item && !g_slist_find(items, item)) - items = g_slist_prepend (items, item); + if (item && items.end()==find(items.begin(),items.end(), item)) + items.push_back(item); } // and now we restore it back @@ -1591,7 +1591,7 @@ static void vacuum_document_recursive(SPObject *obj) unsigned int SPDocument::vacuumDocument() { unsigned int start = objects_in_document(this); - unsigned int end = start; + unsigned int end; unsigned int newend = start; unsigned int iterations = 0; @@ -1634,11 +1634,18 @@ void SPDocument::setModifiedSinceSave(bool modified) { void SPDocument::importDefs(SPDocument *source) { Inkscape::XML::Node *root = source->getReprRoot(); - Inkscape::XML::Node *defs = sp_repr_lookup_name(root, "svg:defs", 1); Inkscape::XML::Node *target_defs = this->getDefs()->getRepr(); + std::vector<Inkscape::XML::Node const *> defsNodes = sp_repr_lookup_name_many(root, "svg:defs"); prevent_id_clashes(source, this); + for (std::vector<Inkscape::XML::Node const *>::iterator defs = defsNodes.begin(); defs != defsNodes.end(); ++defs) { + importDefsNode(source, const_cast<Inkscape::XML::Node *>(*defs), target_defs); + } +} + +void SPDocument::importDefsNode(SPDocument *source, Inkscape::XML::Node *defs, Inkscape::XML::Node *target_defs) +{ int stagger=0; /* Note, "clipboard" throughout the comments means "the document that is either the clipboard diff --git a/src/document.h b/src/document.h index 74dcfa75e..dd1e295a2 100644 --- a/src/document.h +++ b/src/document.h @@ -232,7 +232,7 @@ public: /** * Returns the bottommost item from the list which is at the point, or NULL if none. */ - static SPItem *getItemFromListAtPointBottom(unsigned int dkey, SPGroup *group, const GSList *list, Geom::Point const &p, bool take_insensitive = false); + static SPItem *getItemFromListAtPointBottom(unsigned int dkey, SPGroup *group, const std::vector<SPItem*> &list, Geom::Point const &p, bool take_insensitive = false); static SPDocument *createDoc(Inkscape::XML::Document *rdoc, char const *uri, char const *base, char const *name, unsigned int keepalive, @@ -259,10 +259,10 @@ public: bool addResource(char const *key, SPObject *object); bool removeResource(char const *key, SPObject *object); const GSList *getResourceList(char const *key) const; - GSList *getItemsInBox(unsigned int dkey, Geom::Rect const &box) const; - GSList *getItemsPartiallyInBox(unsigned int dkey, Geom::Rect const &box) const; + std::vector<SPItem*> getItemsInBox(unsigned int dkey, Geom::Rect const &box) const; + std::vector<SPItem*> getItemsPartiallyInBox(unsigned int dkey, Geom::Rect const &box) const; SPItem *getItemAtPoint(unsigned int key, Geom::Point const &p, bool into_groups, SPItem *upto = NULL) const; - GSList *getItemsAtPoints(unsigned const key, std::vector<Geom::Point> points) const; + std::vector<SPItem*> getItemsAtPoints(unsigned const key, std::vector<Geom::Point> points) const; SPItem *getGroupAtPoint(unsigned int key, Geom::Point const &p) const; void changeUriAndHrefs(char const *uri); @@ -275,6 +275,7 @@ public: private: void do_change_uri(char const *const filename, bool const rebase); void setupViewport(SPItemCtx *ctx); + void importDefsNode(SPDocument *source, Inkscape::XML::Node *defs, Inkscape::XML::Node *target_defs); }; /* diff --git a/src/extension/CMakeLists.txt b/src/extension/CMakeLists.txt index 47292fd97..21e652563 100644 --- a/src/extension/CMakeLists.txt +++ b/src/extension/CMakeLists.txt @@ -36,7 +36,6 @@ set(extension_SRC internal/cairo-render-context.cpp internal/cairo-renderer.cpp internal/cairo-renderer-pdf-out.cpp - internal/cdr-input.cpp internal/emf-inout.cpp internal/emf-print.cpp internal/gdkpixbuf-input.cpp @@ -54,7 +53,6 @@ set(extension_SRC internal/svg.cpp internal/svgz.cpp internal/text_reassemble.c - internal/vsd-input.cpp internal/wmf-inout.cpp internal/wmf-print.cpp @@ -103,7 +101,6 @@ set(extension_SRC internal/cairo-render-context.h internal/cairo-renderer-pdf-out.h internal/cairo-renderer.h - internal/cdr-input.h internal/clear-n_.h internal/emf-inout.h internal/emf-print.h @@ -139,7 +136,6 @@ set(extension_SRC internal/svg.h internal/svgz.h internal/text_reassemble.h - internal/vsd-input.h internal/wmf-inout.h internal/wmf-print.h ) @@ -149,6 +145,20 @@ if(WIN32) ) endif() +if(WITH_LIBCDR) + list(APPEND extension_SRC + internal/cdr-input.cpp + internal/cdr-input.h + ) +endif() + +if(WITH_LIBVISIO) + list(APPEND extension_SRC + internal/vsd-input.cpp + internal/vsd-input.h + ) +endif() + if(WITH_LIBWPG) list(APPEND extension_SRC internal/wpg-input.cpp @@ -156,7 +166,7 @@ if(WITH_LIBWPG) ) endif() -if(ImageMagick_FOUND) +if(WITH_IMAGE_MAGICK) list(APPEND extension_SRC internal/bitmap/adaptiveThreshold.cpp internal/bitmap/adaptiveThreshold.h diff --git a/src/extension/dbus/document-interface.cpp b/src/extension/dbus/document-interface.cpp index 4fde6885f..d64bdbc5c 100644 --- a/src/extension/dbus/document-interface.cpp +++ b/src/extension/dbus/document-interface.cpp @@ -186,10 +186,10 @@ selection_get_center_y (Inkscape::Selection *sel){ * know we never bothered to implement it seperatly. Although * they might see the selection box flicker if used in a loop. */ -const GSList * +std::vector<SPObject*> selection_swap(Inkscape::Selection *sel, gchar *name, GError **error) { - const GSList *oldsel = g_slist_copy((GSList *)sel->list()); + std::vector<SPObject*> oldsel = sel->list(); sel->set(get_object_by_name(sel->layers()->getDocument(), name, error)); return oldsel; @@ -199,9 +199,11 @@ selection_swap(Inkscape::Selection *sel, gchar *name, GError **error) * See selection_swap, above */ void -selection_restore(Inkscape::Selection *sel, const GSList * oldsel) +selection_restore(Inkscape::Selection *sel, std::vector<SPObject*> oldsel) { - sel->setList(oldsel); + // ... setList used to work here + sel->clear(); + sel->add(oldsel.begin(), oldsel.end()); } /* @@ -708,8 +710,8 @@ gboolean document_interface_move (DocumentInterface *doc_interface, gchar *name, gdouble x, gdouble y, GError **error) { - const GSList *oldsel = selection_swap(doc_interface->target.getSelection(), name, error); - if (!oldsel) + std::vector<SPObject*> oldsel = selection_swap(doc_interface->target.getSelection(), name, error); + if (oldsel.empty()) return FALSE; sp_selection_move (doc_interface->target.getSelection(), x, 0 - y); selection_restore(doc_interface->target.getSelection(), oldsel); @@ -720,8 +722,8 @@ gboolean document_interface_move_to (DocumentInterface *doc_interface, gchar *name, gdouble x, gdouble y, GError **error) { - const GSList *oldsel = selection_swap(doc_interface->target.getSelection(), name, error); - if (!oldsel) + std::vector<SPObject*> oldsel = selection_swap(doc_interface->target.getSelection(), name, error); + if (oldsel.empty()) return FALSE; Inkscape::Selection * sel = doc_interface->target.getSelection(); sp_selection_move (doc_interface->target.getSelection(), x - selection_get_center_x(sel), @@ -734,8 +736,8 @@ gboolean document_interface_object_to_path (DocumentInterface *doc_interface, char *shape, GError **error) { - const GSList *oldsel = selection_swap(doc_interface->target.getSelection(), shape, error); - if (!oldsel) + std::vector<SPObject*> oldsel = selection_swap(doc_interface->target.getSelection(), shape, error); + if (oldsel.empty()) return FALSE; dbus_call_verb (doc_interface, SP_VERB_OBJECT_TO_CURVE, error); selection_restore(doc_interface->target.getSelection(), oldsel); @@ -849,8 +851,8 @@ gboolean document_interface_move_to_layer (DocumentInterface *doc_interface, gchar *shape, gchar *layerstr, GError **error) { - const GSList *oldsel = selection_swap(doc_interface->target.getSelection(), shape, error); - if (!oldsel) + std::vector<SPObject*> oldsel = selection_swap(doc_interface->target.getSelection(), shape, error); + if (oldsel.empty()) return FALSE; document_interface_selection_move_to_layer(doc_interface, layerstr, error); @@ -1085,15 +1087,15 @@ void document_interface_update(DocumentInterface *doc_interface, GError ** error gboolean document_interface_selection_get(DocumentInterface *doc_interface, char ***out, GError ** /*error*/) { Inkscape::Selection * sel = doc_interface->target.getSelection(); - GSList const *oldsel = sel->list(); + std::vector<SPObject*> oldsel = sel->list(); - int size = g_slist_length((GSList *) oldsel); + int size = oldsel.size(); *out = g_new0 (char *, size + 1); int i = 0; - for (GSList const *iter = oldsel; iter != NULL; iter = iter->next) { - (*out)[i] = g_strdup(SP_OBJECT(iter->data)->getRepr()->attribute("id")); + for (std::vector<SPObject*>::iterator iter = oldsel.begin(), e = oldsel.end(); iter != e; ++iter) { + (*out)[i] = g_strdup((*iter)->getId()); i++; } (*out)[i] = NULL; @@ -1426,23 +1428,21 @@ gboolean dbus_send_ping (SPDesktop* desk, SPItem *item) gboolean document_interface_get_children (DocumentInterface *doc_interface, char *name, char ***out, GError **error) { - SPItem* parent=(SPItem* )get_object_by_name(doc_interface->target.getDocument(), name, error); + SPItem* parent=(SPItem* )get_object_by_name(doc_interface->target.getDocument(), name, error); + std::vector<SPObject*> children = parent->childList(false); - GSList const *children = parent->childList(false); - - int size = g_slist_length((GSList *) children); + int size = children.size(); *out = g_new0 (char *, size + 1); int i = 0; - for (GSList const *iter = children; iter != NULL; iter = iter->next) { - (*out)[i] = g_strdup(SP_OBJECT(iter->data)->getRepr()->attribute("id")); + for (std::vector<SPObject*>::iterator iter = children.begin(), e = children.end(); iter != e; ++iter) { + (*out)[i] = g_strdup((*iter)->getId()); i++; } (*out)[i] = NULL; return TRUE; - } diff --git a/src/extension/execution-env.cpp b/src/extension/execution-env.cpp index 13b8d60c4..29c2b5537 100644 --- a/src/extension/execution-env.cpp +++ b/src/extension/execution-env.cpp @@ -14,10 +14,6 @@ # include <config.h> #endif -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include "gtkmm/messagedialog.h" #include "execution-env.h" @@ -64,14 +60,12 @@ ExecutionEnv::ExecutionEnv (Effect * effect, Inkscape::UI::View::View * doc, Imp sp_namedview_document_from_window(desktop); if (desktop != NULL) { - Inkscape::Util::GSListConstIterator<SPItem *> selected = - desktop->getSelection()->itemList(); - while ( selected != NULL ) { + std::vector<SPItem*> selected = desktop->getSelection()->itemList(); + for(std::vector<SPItem*>::const_iterator x = selected.begin(); x != selected.end(); x++){ Glib::ustring selected_id; - selected_id = (*selected)->getId(); + selected_id = (*x)->getId(); _selected.insert(_selected.end(), selected_id); //std::cout << "Selected: " << selected_id << std::endl; - ++selected; } } diff --git a/src/extension/extension.cpp b/src/extension/extension.cpp index 3d0f49a20..6f7539360 100644 --- a/src/extension/extension.cpp +++ b/src/extension/extension.cpp @@ -19,10 +19,6 @@ # include "config.h" #endif -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include <gtkmm/box.h> #include <gtkmm/label.h> #include <gtkmm/frame.h> diff --git a/src/extension/implementation/implementation.cpp b/src/extension/implementation/implementation.cpp index 52f63499a..b0ff3e91c 100644 --- a/src/extension/implementation/implementation.cpp +++ b/src/extension/implementation/implementation.cpp @@ -47,12 +47,10 @@ Gtk::Widget *Implementation::prefs_effect(Inkscape::Extension::Effect *module, I SPDocument * current_document = view->doc(); - using Inkscape::Util::GSListConstIterator; - // FIXME very unsafe cast - GSListConstIterator<SPItem *> selected = ((SPDesktop *)view)->getSelection()->itemList(); + std::vector<SPItem*> selected = ((SPDesktop *)view)->getSelection()->itemList(); Inkscape::XML::Node const* first_select = NULL; - if (selected != NULL) { - const SPItem * item = *selected; + if (!selected.empty()) { + const SPItem * item = selected[0]; first_select = item->getRepr(); } diff --git a/src/extension/implementation/implementation.h b/src/extension/implementation/implementation.h index fb323cd78..f6f933aaf 100644 --- a/src/extension/implementation/implementation.h +++ b/src/extension/implementation/implementation.h @@ -15,14 +15,7 @@ #include <vector> #include <sigc++/signal.h> #include <glibmm/value.h> - -namespace Geom { - class Affine; - class OptRect; - class Path; - typedef std::vector<Path> PathVector; - class Point; -} +#include <2geom/forward.h> namespace Gtk { class Widget; diff --git a/src/extension/implementation/script.cpp b/src/extension/implementation/script.cpp index bbc567f75..e07a3963c 100644 --- a/src/extension/implementation/script.cpp +++ b/src/extension/implementation/script.cpp @@ -689,14 +689,13 @@ void Script::effect(Inkscape::Extension::Effect *module, return; } - Inkscape::Util::GSListConstIterator<SPItem *> selected = + std::vector<SPItem*> selected = desktop->getSelection()->itemList(); //desktop should not be NULL since doc was checked and desktop is a casted pointer - while ( selected != NULL ) { + for(std::vector<SPItem*>::const_iterator x = selected.begin(); x != selected.end(); x++){ Glib::ustring selected_id; selected_id += "--id="; - selected_id += (*selected)->getId(); + selected_id += (*x)->getId(); params.insert(params.begin(), selected_id); - ++selected; } file_listener fileout; @@ -813,6 +812,12 @@ void Script::copy_doc (Inkscape::XML::Node * oldroot, Inkscape::XML::Node * newr } } + if(!oldroot_namedview) + { + g_warning("Error on copy_doc: No namedview on destination document."); + return; + } + // Unparent (delete) for (unsigned int i = 0; i < delete_list.size(); i++) { sp_repr_unparent(delete_list[i]); @@ -824,12 +829,10 @@ void Script::copy_doc (Inkscape::XML::Node * oldroot, Inkscape::XML::Node * newr 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; - newroot_namedview_child = newroot_namedview_child->next()) { - oldroot_namedview->appendChild(newroot_namedview_child->duplicate(oldroot->document())); - } + for (Inkscape::XML::Node * newroot_namedview_child = child->firstChild(); + newroot_namedview_child != NULL; + newroot_namedview_child = newroot_namedview_child->next()) { + oldroot_namedview->appendChild(newroot_namedview_child->duplicate(oldroot->document())); } } else { oldroot->appendChild(child->duplicate(oldroot->document())); @@ -1027,7 +1030,10 @@ int Script::execute (const std::list<std::string> &in_command, return 0; } - _main_loop = Glib::MainLoop::create(false); + // Create a new MainContext for the loop so that the original context sources are not run here, + // this enforces that only the file_listeners should be read in this new MainLoop + Glib::RefPtr<Glib::MainContext> _main_context = Glib::MainContext::create(); + _main_loop = Glib::MainLoop::create(_main_context, false); file_listener fileerr; fileout.init(stdout_pipe, _main_loop); diff --git a/src/extension/implementation/script.h b/src/extension/implementation/script.h index 6a7d0c3b8..4cf33c989 100644 --- a/src/extension/implementation/script.h +++ b/src/extension/implementation/script.h @@ -90,7 +90,7 @@ private: void init (int fd, Glib::RefPtr<Glib::MainLoop> main) { _channel = Glib::IOChannel::create_from_fd(fd); _channel->set_encoding(); - _conn = Glib::signal_io().connect(sigc::mem_fun(*this, &file_listener::read), _channel, Glib::IO_IN | Glib::IO_HUP | Glib::IO_ERR); + _conn = main->get_context()->signal_io().connect(sigc::mem_fun(*this, &file_listener::read), _channel, Glib::IO_IN | Glib::IO_HUP | Glib::IO_ERR); _main_loop = main; return; diff --git a/src/extension/internal/bitmap/imagemagick.cpp b/src/extension/internal/bitmap/imagemagick.cpp index 76f35415e..bc0dd8e33 100644 --- a/src/extension/internal/bitmap/imagemagick.cpp +++ b/src/extension/internal/bitmap/imagemagick.cpp @@ -12,10 +12,6 @@ # include "config.h" #endif -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include <libintl.h> #include <gtkmm/box.h> @@ -70,8 +66,8 @@ ImageMagickDocCache::ImageMagickDocCache(Inkscape::UI::View::View * view) : _imageItems(NULL) { SPDesktop *desktop = (SPDesktop*)view; - const GSList *selectedItemList = desktop->selection->itemList(); - int selectCount = g_slist_length((GSList *)selectedItemList); + const std::vector<SPItem*> selectedItemList = desktop->selection->itemList(); + int selectCount = selectedItemList.size(); // Init the data-holders _nodes = new Inkscape::XML::Node*[selectCount]; @@ -83,9 +79,8 @@ ImageMagickDocCache::ImageMagickDocCache(Inkscape::UI::View::View * view) : _imageItems = new SPItem*[selectCount]; // Loop through selected items - for (; selectedItemList != NULL; selectedItemList = g_slist_next(selectedItemList)) - { - SPItem *item = SP_ITEM(selectedItemList->data); + for (std::vector<SPItem*>::const_iterator i = selectedItemList.begin(); i != selectedItemList.end(); i++) { + SPItem *item = *i; Inkscape::XML::Node *node = reinterpret_cast<Inkscape::XML::Node *>(item->getRepr()); if (!strcmp(node->name(), "image") || !strcmp(node->name(), "svg:image")) { @@ -241,13 +236,10 @@ ImageMagick::prefs_effect(Inkscape::Extension::Effect *module, Inkscape::UI::Vie { SPDocument * current_document = view->doc(); - using Inkscape::Util::GSListConstIterator; - - // FIXME very unsafe cast - GSListConstIterator<SPItem *> selected = ((SPDesktop *)view)->getSelection()->itemList(); + std::vector<SPItem*> selected = ((SPDesktop *)view)->getSelection()->itemList(); Inkscape::XML::Node * first_select = NULL; - if (selected != NULL) { - first_select = (*selected)->getRepr(); + if (!selected.empty()) { + first_select = (selected.front())->getRepr(); } return module->autogui(current_document, first_select, changeSignal); diff --git a/src/extension/internal/bluredge.cpp b/src/extension/internal/bluredge.cpp index 3ce537d9f..9f19f8b3b 100644 --- a/src/extension/internal/bluredge.cpp +++ b/src/extension/internal/bluredge.cpp @@ -63,13 +63,11 @@ BlurEdge::effect (Inkscape::Extension::Effect *module, Inkscape::UI::View::View Inkscape::Preferences *prefs = Inkscape::Preferences::get(); double old_offset = prefs->getDouble("/options/defaultoffsetwidth/value", 1.0, "px"); - using Inkscape::Util::GSListConstIterator; // TODO need to properly refcount the items, at least - std::list<SPItem *> items; - items.insert<GSListConstIterator<SPItem *> >(items.end(), selection->itemList(), NULL); + std::vector<SPItem*> items(selection->itemList()); selection->clear(); - for(std::list<SPItem *>::iterator item = items.begin(); + for(std::vector<SPItem*>::iterator item = items.begin(); item != items.end(); ++item) { SPItem * spitem = *item; diff --git a/src/extension/internal/cairo-render-context.cpp b/src/extension/internal/cairo-render-context.cpp index 2d6619e1e..c3e416184 100644 --- a/src/extension/internal/cairo-render-context.cpp +++ b/src/extension/internal/cairo-render-context.cpp @@ -1008,17 +1008,16 @@ CairoRenderContext::_createPatternPainter(SPPaintServer const *const paintserver ps2user = Geom::identity(); pcs2dev = Geom::identity(); - double x = pattern_x(pat); - double y = pattern_y(pat); - double width = pattern_width(pat); - double height = pattern_height(pat); + double x = pat->x(); + double y = pat->y(); + double width = pat->width(); + double height = pat->height(); double bbox_width_scaler; double bbox_height_scaler; TRACE(("%f x %f pattern\n", width, height)); - if (pbox && pattern_patternUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) { - //Geom::Affine bbox2user (pbox->x1 - pbox->x0, 0.0, 0.0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0); + if (pbox && pat->patternUnits() == SPPattern::UNITS_OBJECTBOUNDINGBOX) { bbox_width_scaler = pbox->width(); bbox_height_scaler = pbox->height(); ps2user[4] = x * bbox_width_scaler + pbox->left(); @@ -1031,13 +1030,13 @@ CairoRenderContext::_createPatternPainter(SPPaintServer const *const paintserver } // apply pattern transformation - Geom::Affine pattern_transform(pattern_patternTransform(pat)); + Geom::Affine pattern_transform(pat->getTransform()); ps2user *= pattern_transform; Geom::Point ori (ps2user[4], ps2user[5]); // create pattern contents coordinate system if (pat->viewBox_set) { - Geom::Rect view_box = *pattern_viewBox(pat); + Geom::Rect view_box = *pat->viewbox(); double x, y, w, h; x = 0; @@ -1050,7 +1049,7 @@ CairoRenderContext::_createPatternPainter(SPPaintServer const *const paintserver pcs2dev[3] = h / view_box.height(); pcs2dev[4] = x - view_box.left() * pcs2dev[0]; pcs2dev[5] = y - view_box.top() * pcs2dev[3]; - } else if (pbox && pattern_patternContentUnits(pat) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) { + } else if (pbox && pat->patternContentUnits() == SPPattern::UNITS_OBJECTBOUNDINGBOX) { pcs2dev[0] = pbox->width(); pcs2dev[3] = pbox->height(); } @@ -1393,6 +1392,7 @@ CairoRenderContext::_setStrokeStyle(SPStyle const *style, Geom::OptRect const &p dashes[i] = style->stroke_dasharray.values[i]; } cairo_set_dash(_cr, dashes, ndashes, style->stroke_dashoffset.value); + free(dashes); } else { cairo_set_dash(_cr, NULL, 0, 0.0); // disable dashing } diff --git a/src/extension/internal/cairo-renderer.cpp b/src/extension/internal/cairo-renderer.cpp index 7fbdc4296..5a5553e97 100644 --- a/src/extension/internal/cairo-renderer.cpp +++ b/src/extension/internal/cairo-renderer.cpp @@ -294,14 +294,12 @@ static void sp_group_render(SPGroup *group, CairoRenderContext *ctx) CairoRenderer *renderer = ctx->getRenderer(); TRACE(("sp_group_render opacity: %f\n", SP_SCALE24_TO_FLOAT(item->style->opacity.value))); - GSList *l = g_slist_reverse(group->childList(false)); - while (l) { - SPObject *o = reinterpret_cast<SPObject *>(l->data); - SPItem *item = dynamic_cast<SPItem *>(o); + std::vector<SPObject*> l(group->childList(false)); + for(std::vector<SPObject*>::const_iterator x = l.begin(); x!= l.end(); x++){ + SPItem *item = dynamic_cast<SPItem*>(*x); if (item) { renderer->renderItem(ctx, item); } - l = g_slist_remove (l, o); } } diff --git a/src/extension/internal/cdr-input.h b/src/extension/internal/cdr-input.h index 3de6c1ed0..10af41d5a 100644 --- a/src/extension/internal/cdr-input.h +++ b/src/extension/internal/cdr-input.h @@ -19,10 +19,6 @@ #ifdef WITH_LIBCDR -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include <gtkmm/dialog.h> #include "../implementation/implementation.h" diff --git a/src/extension/internal/emf-inout.cpp b/src/extension/internal/emf-inout.cpp index 31e69706f..e88cf3d42 100644 --- a/src/extension/internal/emf-inout.cpp +++ b/src/extension/internal/emf-inout.cpp @@ -480,7 +480,7 @@ uint32_t Emf::add_image(PEMF_CALLBACK_DATA d, void *pEmr, uint32_t cbBits, uint uint32_t width, height, colortype, numCt, invert; // if needed these values will be set in get_DIB_params if(cbBits && cbBmi && (iUsage == U_DIB_RGB_COLORS)){ // next call returns pointers and values, but allocates no memory - dibparams = get_DIB_params(pEmr, offBits, offBmi, &px, (const U_RGBQUAD **) &ct, + dibparams = get_DIB_params((const char *)pEmr, offBits, offBmi, &px, (const U_RGBQUAD **) &ct, &numCt, &width, &height, &colortype, &invert); if(dibparams ==U_BI_RGB){ // U_EMRCREATEMONOBRUSH uses text/bk colors instead of what is in the color map. @@ -1307,7 +1307,7 @@ Emf::select_extpen(PEMF_CALLBACK_DATA d, int index) d->dc[d->level].stroke_set = true; } else if(pEmr->elp.elpBrushStyle == U_BS_DIBPATTERN || pEmr->elp.elpBrushStyle == U_BS_DIBPATTERNPT){ - d->dc[d->level].stroke_idx = add_image(d, pEmr, pEmr->cbBits, pEmr->cbBmi, *(uint32_t *) &(pEmr->elp.elpColor), pEmr->offBits, pEmr->offBmi); + d->dc[d->level].stroke_idx = add_image(d, (void *)pEmr, pEmr->cbBits, pEmr->cbBmi, *(uint32_t *) &(pEmr->elp.elpColor), pEmr->offBits, pEmr->offBmi); d->dc[d->level].stroke_mode = DRAW_IMAGE; d->dc[d->level].stroke_set = true; } @@ -1530,7 +1530,7 @@ void Emf::common_image_extraction(PEMF_CALLBACK_DATA d, void *pEmr, uint32_t width, height, colortype, numCt, invert; // if needed these values will be set in get_DIB_params if(cbBits && cbBmi && (iUsage == U_DIB_RGB_COLORS)){ // next call returns pointers and values, but allocates no memory - dibparams = get_DIB_params(pEmr, offBits, offBmi, &px, (const U_RGBQUAD **) &ct, + dibparams = get_DIB_params((const char *)pEmr, offBits, offBmi, &px, (const U_RGBQUAD **) &ct, &numCt, &width, &height, &colortype, &invert); if(dibparams ==U_BI_RGB){ if(sw == 0 || sh == 0){ @@ -1611,6 +1611,10 @@ int Emf::myEnhMetaFileProc(char *contents, unsigned int length, PEMF_CALLBACK_DA uint32_t off=0; uint32_t emr_mask; int OK =1; + int file_status=1; + uint32_t nSize; + uint32_t iType; + const char *blimit = contents + length; PU_ENHMETARECORD lpEMFR; TCHUNK_SPECS tsp; uint32_t tbkMode = U_TRANSPARENT; // holds proposed change to bkMode, if text is involved saving these to the DC must wait until the text is written @@ -1641,19 +1645,32 @@ int Emf::myEnhMetaFileProc(char *contents, unsigned int length, PEMF_CALLBACK_DA while(OK){ if(off>=length)return(0); //normally should exit from while after EMREOF sets OK to false. + // check record sizes and types thoroughly + int badrec = 0; + if (!U_emf_record_sizeok(contents + off, blimit, &nSize, &iType, 1) || + !U_emf_record_safe(contents + off)){ + badrec = 1; + } + else { + emr_mask = emr_properties(iType); + if (emr_mask == U_EMR_INVALID) { badrec = 1; } + } + if (badrec) { + file_status = 0; + break; + } + lpEMFR = (PU_ENHMETARECORD)(contents + off); + // Uncomment the following to track down toxic records -// std::cout << "record type: " << lpEMFR->iType << " name " << U_emr_names(lpEMFR->iType) << " length: " << lpEMFR->nSize << " offset: " << off <<std::endl; - off += lpEMFR->nSize; +// std::cout << "record type: " << iType << " name " << U_emr_names(iType) << " length: " << nSize << " offset: " << off <<std::endl; + off += nSize; SVGOStringStream tmp_outsvg; SVGOStringStream tmp_path; SVGOStringStream tmp_str; SVGOStringStream dbg_str; - emr_mask = emr_properties(lpEMFR->iType); - if(emr_mask == U_EMR_INVALID){ throw "Inkscape fatal memory allocation error - cannot continue"; } - /* Uncomment the following to track down text problems */ //std::cout << "tri->dirty:"<< d->tri->dirty << " emr_mask: " << std::hex << emr_mask << std::dec << std::endl; @@ -1753,7 +1770,7 @@ std::cout << "BEFORE DRAW" } // std::cout << "AFTER DRAW logic d->mask: " << std::hex << d->mask << " emr_mask: " << emr_mask << std::dec << std::endl; - switch (lpEMFR->iType) + switch (iType) { case U_EMR_HEADER: { @@ -3471,7 +3488,7 @@ std::cout << "BEFORE DRAW" // std::cout << d->outsvg << std::endl; (void) emr_properties(U_EMR_INVALID); // force the release of the lookup table memory, returned value is irrelevant - return 1; + return(file_status); } void Emf::free_emf_strings(EMF_STRINGS name){ @@ -3486,12 +3503,14 @@ void Emf::free_emf_strings(EMF_STRINGS name){ SPDocument * Emf::open( Inkscape::Extension::Input * /*mod*/, const gchar *uri ) { - EMF_CALLBACK_DATA d; - if (uri == NULL) { return NULL; } + EMF_CALLBACK_DATA d; + + d.n_obj = 0; //these might not be set otherwise if the input file is corrupt + d.emf_obj = NULL; d.dc[0].font_name = strdup("Arial"); // Default font, set only on lowest level, it copies up from there EMF spec says device can pick whatever it wants // set up the size default for patterns in defs. This might not be referenced if there are no patterns defined in the drawing. @@ -3518,14 +3537,17 @@ Emf::open( Inkscape::Extension::Input * /*mod*/, const gchar *uri ) FT_LOAD_NO_SCALE | FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP, FT_KERNING_UNSCALED); - (void) myEnhMetaFileProc(contents,length, &d); + int good = myEnhMetaFileProc(contents,length, &d); free(contents); if (d.pDesc){ free( d.pDesc ); } // std::cout << "SVG Output: " << std::endl << d.outsvg << std::endl; - SPDocument *doc = SPDocument::createNewDocFromMem(d.outsvg.c_str(), strlen(d.outsvg.c_str()), TRUE); + SPDocument *doc = NULL; + if (good) { + doc = SPDocument::createNewDocFromMem(d.outsvg.c_str(), strlen(d.outsvg.c_str()), TRUE); + } free_emf_strings(d.hatches); free_emf_strings(d.images); diff --git a/src/extension/internal/emf-inout.h b/src/extension/internal/emf-inout.h index c64299093..dd1d8f9c4 100644 --- a/src/extension/internal/emf-inout.h +++ b/src/extension/internal/emf-inout.h @@ -13,6 +13,8 @@ #define SEEN_EXTENSION_INTERNAL_EMF_H #include <libuemf/uemf.h> +#include <libuemf/uemf_safe.h> +#include <libuemf/uemf_endian.h> // for U_emf_record_sizeok() #include "extension/internal/metafile-inout.h" // picks up PNG #include "extension/implementation/implementation.h" #include "style.h" diff --git a/src/extension/internal/emf-print.cpp b/src/extension/internal/emf-print.cpp index 67a9242bc..5b8aae655 100644 --- a/src/extension/internal/emf-print.cpp +++ b/src/extension/internal/emf-print.cpp @@ -60,6 +60,7 @@ #include "splivarot.h" // pieces for union on shapes #include "2geom/svg-path-parser.h" // to get from SVG text to Geom::Path #include "display/canvas-bpath.h" // for SPWindRule +#include "display/cairo-utils.h" // for Inkscape::Pixbuf::PF_CAIRO #include "emf-print.h" @@ -70,7 +71,6 @@ namespace Internal { #define PXPERMETER 2835 - /* globals */ static double PX2WORLD; static bool FixPPTCharPos, FixPPTDashLine, FixPPTGrad2Polys, FixPPTLinGrad, FixPPTPatternAsHatch, FixImageRot; @@ -388,8 +388,8 @@ int PrintEmf::create_brush(SPStyle const *style, PU_COLORREF fcolor) } else if (SP_IS_PATTERN(SP_STYLE_FILL_SERVER(style))) { // must be paint-server SPPaintServer *paintserver = style->fill.value.href->getObject(); SPPattern *pat = SP_PATTERN(paintserver); - double dwidth = pattern_width(pat); - double dheight = pattern_height(pat); + double dwidth = pat->width(); + double dheight = pat->height(); width = dwidth; height = dheight; brush_classify(pat, 0, &pixbuf, &hatchType, &hatchColor, &bkColor); @@ -481,9 +481,8 @@ int PrintEmf::create_brush(SPStyle const *style, PU_COLORREF fcolor) rgba_px = (char *) pixbuf->pixels(); // Do NOT free this!!! colortype = U_BCBM_COLOR32; (void) RGBA_to_DIB(&px, &cbPx, &ct, &numCt, rgba_px, width, height, width * 4, colortype, 0, 1); - // Not sure why the next swap is needed because the preceding does it, and the code is identical - // to that in stretchdibits_set, which does not need this. - swapRBinRGBA(px, width * height); + // pixbuf can be either PF_CAIRO or PF_GDK, and these have R and B bytes swapped + if (pixbuf->pixelFormat() == Inkscape::Pixbuf::PF_CAIRO) { swapRBinRGBA(px, width * height); } Bmih = bitmapinfoheader_set(width, height, 1, colortype, U_BI_RGB, 0, PXPERMETER, PXPERMETER, numCt, 0); Bmi = bitmapinfo_set(Bmih, ct); rec = createdibpatternbrushpt_set(&brush, eht, U_DIB_RGB_COLORS, Bmi, cbPx, px); @@ -574,8 +573,8 @@ int PrintEmf::create_pen(SPStyle const *style, const Geom::Affine &transform) if (SP_IS_PATTERN(SP_STYLE_STROKE_SERVER(style))) { // must be paint-server SPPaintServer *paintserver = style->stroke.value.href->getObject(); SPPattern *pat = SP_PATTERN(paintserver); - double dwidth = pattern_width(pat); - double dheight = pattern_height(pat); + double dwidth = pat->width(); + double dheight = pat->height(); width = dwidth; height = dheight; brush_classify(pat, 0, &pixbuf, &hatchType, &hatchColor, &bkColor); @@ -584,9 +583,8 @@ int PrintEmf::create_pen(SPStyle const *style, const Geom::Affine &transform) rgba_px = (char *) pixbuf->pixels(); // Do NOT free this!!! colortype = U_BCBM_COLOR32; (void) RGBA_to_DIB(&px, &cbPx, &ct, &numCt, rgba_px, width, height, width * 4, colortype, 0, 1); - // Not sure why the next swap is needed because the preceding does it, and the code is identical - // to that in stretchdibits_set, which does not need this. - swapRBinRGBA(px, width * height); + // pixbuf can be either PF_CAIRO or PF_GDK, and these have R and B bytes swapped + if (pixbuf->pixelFormat() == Inkscape::Pixbuf::PF_CAIRO) { swapRBinRGBA(px, width * height); } Bmih = bitmapinfoheader_set(width, height, 1, colortype, U_BI_RGB, 0, PXPERMETER, PXPERMETER, numCt, 0); Bmi = bitmapinfo_set(Bmih, ct); } else { // pattern @@ -862,7 +860,7 @@ Geom::Path PrintEmf::pathv_to_rect(Geom::PathVector const &pathv, bool *is_rect, /* Get the ends of the LAST line segment. Find minimum rotation to align rectangle with X,Y axes. (Very degenerate if it is rotated 45 degrees.) */ *angle = 10.0; /* must be > than the actual angle in radians. */ - for(Geom::Path::const_iterator cit = pR.begin(); cit != pR.end_open(); ++cit){ + for(Geom::Path::iterator cit = pR.begin(); cit != pR.end_open(); ++cit){ P1_trail = cit->initialPoint(); P1 = cit->finalPoint(); v1 = unit_vector(P1 - P1_trail); @@ -876,7 +874,7 @@ Geom::Path PrintEmf::pathv_to_rect(Geom::PathVector const &pathv, bool *is_rect, double convert = 36000.0/ (2.0 * M_PI); *angle = round(*angle * convert)/convert; - for(Geom::Path::const_iterator cit = pR.begin(); cit != pR.end_open();++cit) { + for(Geom::Path::iterator cit = pR.begin(); cit != pR.end_open();++cit) { P1_lead = cit->finalPoint(); v1 = unit_vector(P1 - P1_trail); v2 = unit_vector(P1_lead - P1 ); @@ -926,7 +924,7 @@ int PrintEmf::vector_rect_alignment(double angle, Geom::Point vtest){ */ Geom::Point PrintEmf::get_pathrect_corner(Geom::Path pathRect, double angle, int corner){ Geom::Point center(0,0); - for(Geom::Path::const_iterator cit = pathRect.begin(); cit != pathRect.end_open(); ++cit) { + for(Geom::Path::iterator cit = pathRect.begin(); cit != pathRect.end_open(); ++cit) { center += cit->initialPoint()/4.0; } @@ -954,7 +952,7 @@ Geom::Point PrintEmf::get_pathrect_corner(Geom::Path pathRect, double angle, int Geom::Point v1 = Geom::Point(1,0) * Geom::Rotate(-angle); // unit horizontal side (sign change because Y increases DOWN) Geom::Point v2 = Geom::Point(0,1) * Geom::Rotate(-angle); // unit vertical side (sign change because Y increases DOWN) Geom::Point P1; - for(Geom::Path::const_iterator cit = pathRect.begin(); cit != pathRect.end_open(); ++cit) { + for(Geom::Path::iterator cit = pathRect.begin(); cit != pathRect.end_open(); ++cit) { P1 = cit->initialPoint(); if ( ( LR == (dot(P1 - center,v1) > 0 ? 0 : 1) ) @@ -1496,11 +1494,11 @@ bool PrintEmf::print_simple_shape(Geom::PathVector const &pathv, const Geom::Aff int curves = 0; char *rec = NULL; - for (Geom::PathVector::const_iterator pit = pv.begin(); pit != pv.end(); ++pit) { + for (Geom::PathVector::iterator pit = pv.begin(); pit != pv.end(); ++pit) { moves++; nodes++; - for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_open(); ++cit) { + for (Geom::Path::iterator cit = pit->begin(); cit != pit->end_open(); ++cit) { nodes++; if (is_straight_curve(*cit)) { @@ -1521,7 +1519,7 @@ bool PrintEmf::print_simple_shape(Geom::PathVector const &pathv, const Geom::Aff /** * For all Subpaths in the <path> */ - for (Geom::PathVector::const_iterator pit = pv.begin(); pit != pv.end(); ++pit) { + for (Geom::PathVector::iterator pit = pv.begin(); pit != pv.end(); ++pit) { using Geom::X; using Geom::Y; @@ -1540,7 +1538,7 @@ bool PrintEmf::print_simple_shape(Geom::PathVector const &pathv, const Geom::Aff /** * For all segments in the subpath */ - for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_open(); ++cit) { + for (Geom::Path::iterator cit = pit->begin(); cit != pit->end_open(); ++cit) { if (is_straight_curve(*cit)) { //Geom::Point p0 = cit->initialPoint(); Geom::Point p1 = cit->finalPoint(); @@ -1559,7 +1557,7 @@ bool PrintEmf::print_simple_shape(Geom::PathVector const &pathv, const Geom::Aff lpPoints[i].y = y1; i = i + 1; } else if (Geom::CubicBezier const *cubic = dynamic_cast<Geom::CubicBezier const *>(&*cit)) { - std::vector<Geom::Point> points = cubic->points(); + std::vector<Geom::Point> points = cubic->controlPoints(); //Geom::Point p0 = points[0]; Geom::Point p1 = points[1]; Geom::Point p2 = points[2]; @@ -1848,7 +1846,7 @@ unsigned int PrintEmf::draw_pathv_to_EMF(Geom::PathVector const &pathv, const Ge g_error("Fatal programming error in PrintEmf::print_pathv at U_EMRLINETO_set"); } } else if (Geom::CubicBezier const *cubic = dynamic_cast<Geom::CubicBezier const *>(&*cit)) { - std::vector<Geom::Point> points = cubic->points(); + std::vector<Geom::Point> points = cubic->controlPoints(); //Geom::Point p0 = points[0]; Geom::Point p1 = points[1]; Geom::Point p2 = points[2]; diff --git a/src/extension/internal/filter/color.h b/src/extension/internal/filter/color.h index c3c26bf8b..b9c76615c 100644 --- a/src/extension/internal/filter/color.h +++ b/src/extension/internal/filter/color.h @@ -258,7 +258,7 @@ public: "<param name=\"type\" gui-text=\"" N_("Blindness type:") "\" type=\"enum\">\n" "<_item value=\"0.618 0.32 0.062 0 0 0.163 0.775 0.062 0 0 0.163 0.32 0.516 0 0 0 0 0 1 0 \">" N_("Rod monochromacy (atypical achromatopsia)") "</_item>\n" "<_item value=\"0.299 0.587 0.114 0 0 0.299 0.587 0.114 0 0 0.299 0.587 0.114 0 0 0 0 0 1 0 \">" N_("Cone monochromacy (typical achromatopsia)") "</_item>\n" - "<_item value=\"0.8 0.2 0 0 0 0.2583 0.74167 0 0 0 0 0.14167 0.85833 0 0 0 0 0 1 0 \">" N_("Geen weak (deuteranomaly)") "</_item>\n" + "<_item value=\"0.8 0.2 0 0 0 0.2583 0.74167 0 0 0 0 0.14167 0.85833 0 0 0 0 0 1 0 \">" N_("Green weak (deuteranomaly)") "</_item>\n" "<_item value=\"0.625 0.375 0 0 0 0.7 0.3 0 0 0 0 0.3 0.7 0 0 0 0 0 1 0 \">" N_("Green blind (deuteranopia)") "</_item>\n" "<_item value=\"0.8166 0.1833 0 0 0 0.333 0.666 0 0 0 0 0.125 0.875 0 0 0 0 0 1 0 \">" N_("Red weak (protanomaly)") "</_item>\n" "<_item value=\"0.566 0.43333 0 0 0 0.55833 0.4416 0 0 0 0 0.24167 0.75833 0 0 0 0 0 1 0 \">" N_("Red blind (protanopia)") "</_item>\n" diff --git a/src/extension/internal/filter/filter.cpp b/src/extension/internal/filter/filter.cpp index a2c565699..65162af22 100644 --- a/src/extension/internal/filter/filter.cpp +++ b/src/extension/internal/filter/filter.cpp @@ -125,15 +125,13 @@ void Filter::effect(Inkscape::Extension::Effect *module, Inkscape::UI::View::Vie //printf("Calling filter effect\n"); Inkscape::Selection * selection = ((SPDesktop *)document)->selection; - using Inkscape::Util::GSListConstIterator; // TODO need to properly refcount the items, at least - std::list<SPItem *> items; - items.insert<GSListConstIterator<SPItem *> >(items.end(), selection->itemList(), NULL); + std::vector<SPItem*> items(selection->itemList()); Inkscape::XML::Document * xmldoc = document->doc()->getReprDoc(); Inkscape::XML::Node * defsrepr = document->doc()->getDefs()->getRepr(); - for(std::list<SPItem *>::iterator item = items.begin(); + for(std::vector<SPItem*>::iterator item = items.begin(); item != items.end(); ++item) { SPItem * spitem = *item; Inkscape::XML::Node * node = spitem->getRepr(); diff --git a/src/extension/internal/grid.cpp b/src/extension/internal/grid.cpp index 270edfe44..8fd82b675 100644 --- a/src/extension/internal/grid.cpp +++ b/src/extension/internal/grid.cpp @@ -15,10 +15,6 @@ # include <config.h> #endif -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include <gtkmm/box.h> #include <gtkmm/adjustment.h> #include <gtkmm/spinbutton.h> @@ -190,13 +186,10 @@ Grid::prefs_effect(Inkscape::Extension::Effect *module, Inkscape::UI::View::View { SPDocument * current_document = view->doc(); - using Inkscape::Util::GSListConstIterator; - - // FIXME very unsafe cast - GSListConstIterator<SPItem *> selected = ((SPDesktop *)view)->getSelection()->itemList(); + std::vector<SPItem*> selected = ((SPDesktop *)view)->getSelection()->itemList(); Inkscape::XML::Node * first_select = NULL; - if (selected != NULL) { - first_select = (*selected)->getRepr(); + if (!selected.empty()) { + first_select = selected[0]->getRepr(); } return module->autogui(current_document, first_select, changeSignal); diff --git a/src/extension/internal/javafx-out.cpp b/src/extension/internal/javafx-out.cpp index 19946022c..386bde1d6 100644 --- a/src/extension/internal/javafx-out.cpp +++ b/src/extension/internal/javafx-out.cpp @@ -35,8 +35,7 @@ #include <extension/system.h> #include <2geom/pathvector.h> #include <2geom/rect.h> -#include <2geom/bezier-curve.h> -#include <2geom/hvlinesegment.h> +#include <2geom/curves.h> #include "helper/geom.h" #include "helper/geom-curves.h" #include <io/sys.h> @@ -531,9 +530,7 @@ bool JavaFXOutput::doCurve(SPItem *item, const String &id) for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_closed(); ++cit) { //### LINE - if ( dynamic_cast<Geom::LineSegment const *> (&*cit) || - dynamic_cast<Geom::HLineSegment const *> (&*cit) || - dynamic_cast<Geom::VLineSegment const *> (&*cit) ) + if ( dynamic_cast<Geom::LineSegment const *> (&*cit) ) { Geom::Point p = cit->finalPoint(); out(" LineTo {\n"); @@ -545,7 +542,7 @@ bool JavaFXOutput::doCurve(SPItem *item, const String &id) //### BEZIER else if (Geom::CubicBezier const *cubic = dynamic_cast<Geom::CubicBezier const*>(&*cit)) { - std::vector<Geom::Point> points = cubic->points(); + std::vector<Geom::Point> points = cubic->controlPoints(); Geom::Point p1 = points[1]; Geom::Point p2 = points[2]; Geom::Point p3 = points[3]; diff --git a/src/extension/internal/latex-pstricks.cpp b/src/extension/internal/latex-pstricks.cpp index 6aaa1bca4..ae8f30a5c 100644 --- a/src/extension/internal/latex-pstricks.cpp +++ b/src/extension/internal/latex-pstricks.cpp @@ -17,8 +17,7 @@ #include <2geom/pathvector.h> #include <2geom/sbasis-to-bezier.h> -#include <2geom/bezier-curve.h> -#include <2geom/hvlinesegment.h> +#include <2geom/curves.h> #include <errno.h> #include <signal.h> #include "util/units.h" @@ -300,7 +299,7 @@ PrintLatex::print_2geomcurve(SVGOStringStream &os, Geom::Curve const &c) os << "\\lineto(" << c.finalPoint()[X] << "," << c.finalPoint()[Y] << ")\n"; } else if(Geom::CubicBezier const *cubic_bezier = dynamic_cast<Geom::CubicBezier const*>(&c)) { - std::vector<Geom::Point> points = cubic_bezier->points(); + std::vector<Geom::Point> points = cubic_bezier->controlPoints(); os << "\\curveto(" << points[1][X] << "," << points[1][Y] << ")(" << points[2][X] << "," << points[2][Y] << ")(" << points[3][X] << "," << points[3][Y] << ")\n"; diff --git a/src/extension/internal/latex-text-renderer.cpp b/src/extension/internal/latex-text-renderer.cpp index ab0733848..5933dd526 100644 --- a/src/extension/internal/latex-text-renderer.cpp +++ b/src/extension/internal/latex-text-renderer.cpp @@ -228,14 +228,12 @@ LaTeXTextRenderer::writePostamble() void LaTeXTextRenderer::sp_group_render(SPGroup *group) { - GSList *l = g_slist_reverse(group->childList(false)); - while (l) { - SPObject *o = reinterpret_cast<SPObject *>(l->data); - SPItem *item = dynamic_cast<SPItem *>(o); + std::vector<SPObject*> l = (group->childList(false)); + for(std::vector<SPObject*>::const_iterator x = l.begin(); x != l.end(); x++){ + SPItem *item = dynamic_cast<SPItem*>(*x); if (item) { renderItem(item); } - l = g_slist_remove (l, o); } } @@ -411,7 +409,8 @@ Flowing in rectangle is possible, not in arb shape. return; // don't know how to handle non-rect frames yet. is quite uncommon for latex users i think } - Geom::Rect framebox = frame->getRect() * transform(); + // We will transform the coordinates + Geom::Rect framebox = frame->getRect(); // get position and alignment // Align on topleft corner. @@ -431,7 +430,10 @@ Flowing in rectangle is possible, not in arb shape. // no need to add LaTeX code for standard justified output :) break; } - Geom::Point pos(framebox.corner(3)); //topleft corner + + // The topleft Corner was calculated after rotating the text which results in a wrong Coordinate. + // Now, the topleft Corner is rotated after calculating it + Geom::Point pos(framebox.corner(0) * transform()); //topleft corner // determine color and transparency (for now, use rgb color model as it is most native to Inkscape) bool has_color = false; // if the item has no color set, don't force black color @@ -474,7 +476,9 @@ Flowing in rectangle is possible, not in arb shape. os << "\\rotatebox{" << degrees << "}{"; } os << "\\makebox(0,0)" << alignment << "{"; - os << "\\begin{minipage}{" << framebox.width() << "\\unitlength}"; + + // Scale the x width correctly + os << "\\begin{minipage}{" << framebox.width() * transform().expansionX() << "\\unitlength}"; os << justification; // Walk through all spans in the text object. diff --git a/src/extension/internal/metafile-print.cpp b/src/extension/internal/metafile-print.cpp index 73d63f27d..2fb36be85 100644 --- a/src/extension/internal/metafile-print.cpp +++ b/src/extension/internal/metafile-print.cpp @@ -389,7 +389,7 @@ Geom::PathVector PrintMetafile::center_ellipse_as_SVG_PathV(Geom::Point ctr, dou char text[256]; sprintf(text, " M %f,%f A %f %f %f 0 0 %f %f A %f %f %f 0 0 %f %f z", x1, y1, rx, ry, F * 360. / (2.*M_PI), x2, y2, rx, ry, F * 360. / (2.*M_PI), x1, y1); - std::vector<Geom::Path> outres = Geom::parse_svg_path(text); + Geom::PathVector outres = Geom::parse_svg_path(text); return outres; } @@ -419,7 +419,7 @@ Geom::PathVector PrintMetafile::center_elliptical_ring_as_SVG_PathV(Geom::Point sprintf(text, " M %f,%f A %f %f %f 0 1 %f %f A %f %f %f 0 1 %f %f z M %f,%f A %f %f %f 0 0 %f %f A %f %f %f 0 0 %f %f z", x11, y11, rx1, ry1, degrot, x12, y12, rx1, ry1, degrot, x11, y11, x21, y21, rx2, ry2, degrot, x22, y22, rx2, ry2, degrot, x21, y21); - std::vector<Geom::Path> outres = Geom::parse_svg_path(text); + Geom::PathVector outres = Geom::parse_svg_path(text); return outres; } @@ -440,7 +440,7 @@ Geom::PathVector PrintMetafile::center_elliptical_hole_as_SVG_PathV(Geom::Point char text[256]; sprintf(text, " M %f,%f A %f %f %f 0 0 %f %f A %f %f %f 0 0 %f %f z M 50000,50000 50000,-50000 -50000,-50000 -50000,50000 z", x1, y1, rx, ry, F * 360. / (2.*M_PI), x2, y2, rx, ry, F * 360. / (2.*M_PI), x1, y1); - std::vector<Geom::Path> outres = Geom::parse_svg_path(text); + Geom::PathVector outres = Geom::parse_svg_path(text); return outres; } @@ -452,7 +452,7 @@ width vector to side edge */ Geom::PathVector PrintMetafile::rect_cutter(Geom::Point ctr, Geom::Point pos, Geom::Point neg, Geom::Point width) { - std::vector<Geom::Path> outres; + Geom::PathVector outres; Geom::Path cutter; cutter.start(ctr + pos - width); cutter.appendNew<Geom::LineSegment>(ctr + pos + width); diff --git a/src/extension/internal/odf.cpp b/src/extension/internal/odf.cpp index 52fabcf3c..6904eb2fd 100644 --- a/src/extension/internal/odf.cpp +++ b/src/extension/internal/odf.cpp @@ -55,8 +55,7 @@ #include <style.h> #include "display/curve.h" #include <2geom/pathvector.h> -#include <2geom/bezier-curve.h> -#include <2geom/hvlinesegment.h> +#include <2geom/curves.h> #include <2geom/transforms.h> #include <helper/geom.h> #include "helper/geom-curves.h" @@ -1299,7 +1298,7 @@ writePath(Writer &outs, Geom::PathVector const &pathv, outs.printf("L %.3f %.3f ", destx, desty); } else if(Geom::CubicBezier const *cubic = dynamic_cast<Geom::CubicBezier const*>(&*cit)) { - std::vector<Geom::Point> points = cubic->points(); + std::vector<Geom::Point> points = cubic->controlPoints(); for (unsigned i = 1; i <= 3; i++) { if (fabs(points[i][X])<1.0) points[i][X] = 0.0; // Why is this needed? Shouldn't we just round all numbers then? if (fabs(points[i][Y])<1.0) points[i][Y] = 0.0; diff --git a/src/extension/internal/pdfinput/pdf-input.cpp b/src/extension/internal/pdfinput/pdf-input.cpp index 148d2dcba..363061734 100644 --- a/src/extension/internal/pdfinput/pdf-input.cpp +++ b/src/extension/internal/pdfinput/pdf-input.cpp @@ -36,8 +36,14 @@ #include <gtkmm/comboboxtext.h> #include <gtkmm/drawingarea.h> #include <gtkmm/frame.h> +#include <gtkmm/radiobutton.h> #include <gtkmm/scale.h> +#if WITH_GTKMM_3_0 +#include <glibmm/convert.h> +#include <glibmm/miscutils.h> +#endif + #include "extension/system.h" #include "extension/input.h" #include "svg-builder.h" @@ -81,7 +87,6 @@ PdfImportDialog::PdfImportDialog(PDFDoc *doc, const gchar */*uri*/) _poppler_doc = NULL; #endif // HAVE_POPPLER_CAIRO _pdf_doc = doc; - cancelbutton = Gtk::manage(new class Gtk::Button(Gtk::StockID("gtk-cancel"))); okbutton = Gtk::manage(new class Gtk::Button(Gtk::StockID("gtk-ok"))); _labelSelect = Gtk::manage(new class Gtk::Label(_("Select page:"))); @@ -125,8 +130,13 @@ PdfImportDialog::PdfImportDialog(PDFDoc *doc, const gchar */*uri*/) _labelPrecisionWarning = Gtk::manage(new class Gtk::Label(_("<b>Note</b>: setting the precision too high may result in a large SVG file and slow performance."))); #ifdef HAVE_POPPLER_CAIRO - _importviaPopplerCheck = Gtk::manage(new class Gtk::CheckButton(_("import via Poppler"))); + Gtk::RadioButton::Group group; + _importViaPoppler = Gtk::manage(new class Gtk::RadioButton(group,_("Poppler/Cairo import"))); + _labelViaPoppler = Gtk::manage(new class Gtk::Label(_("Import via external library. Text consists of groups containing cloned glyphs where each glyph is a path. Images are stored internally. Meshes cause entire document to be rendered as a raster image."))); + _importViaInternal = Gtk::manage(new class Gtk::RadioButton(group,_("Internal import"))); + _labelViaInternal = Gtk::manage(new class Gtk::Label(_("Import via internal (Poppler derived) library. Text is stored as text but white space is missing. Meshes are converted to tiles, the number depends on the precision set below."))); #endif + #if WITH_GTKMM_3_0 _fallbackPrecisionSlider_adj = Gtk::Adjustment::create(2, 1, 256, 1, 10, 10); _fallbackPrecisionSlider = Gtk::manage(new class Gtk::Scale(_fallbackPrecisionSlider_adj)); @@ -139,13 +149,15 @@ PdfImportDialog::PdfImportDialog(PDFDoc *doc, const gchar */*uri*/) hbox6 = Gtk::manage(new class Gtk::HBox(false, 4)); // Text options - _labelText = Gtk::manage(new class Gtk::Label(_("Text handling:"))); - _textHandlingCombo = Gtk::manage(new class Gtk::ComboBoxText()); - _textHandlingCombo->append(_("Import text as text")); - _textHandlingCombo->set_active_text(_("Import text as text")); + // _labelText = Gtk::manage(new class Gtk::Label(_("Text handling:"))); + // _textHandlingCombo = Gtk::manage(new class Gtk::ComboBoxText()); + // _textHandlingCombo->append(_("Import text as text")); + // _textHandlingCombo->set_active_text(_("Import text as text")); + // hbox5 = Gtk::manage(new class Gtk::HBox(false, 4)); + + // Font option _localFontsCheck = Gtk::manage(new class Gtk::CheckButton(_("Replace PDF fonts by closest-named installed fonts"))); - hbox5 = Gtk::manage(new class Gtk::HBox(false, 4)); _embedImagesCheck = Gtk::manage(new class Gtk::CheckButton(_("Embed images"))); vbox3 = Gtk::manage(new class Gtk::VBox(false, 4)); _importSettingsFrame = Gtk::manage(new class Inkscape::UI::Widget::Frame(_("Import settings"))); @@ -202,12 +214,19 @@ PdfImportDialog::PdfImportDialog(PDFDoc *doc, const gchar */*uri*/) _labelPrecisionWarning->set_line_wrap(true); _labelPrecisionWarning->set_use_markup(true); _labelPrecisionWarning->set_selectable(false); + #ifdef HAVE_POPPLER_CAIRO - _importviaPopplerCheck->set_can_focus(); - _importviaPopplerCheck->set_relief(Gtk::RELIEF_NORMAL); - _importviaPopplerCheck->set_mode(true); - _importviaPopplerCheck->set_active(false); + _importViaPoppler->set_can_focus(); + _importViaPoppler->set_relief(Gtk::RELIEF_NORMAL); + _importViaPoppler->set_mode(true); + _importViaPoppler->set_active(false); + _importViaInternal->set_can_focus(); + _importViaInternal->set_relief(Gtk::RELIEF_NORMAL); + _importViaInternal->set_mode(true); + _labelViaPoppler->set_line_wrap(true); + _labelViaInternal->set_line_wrap(true); #endif + _fallbackPrecisionSlider->set_size_request(180,-1); _fallbackPrecisionSlider->set_can_focus(); _fallbackPrecisionSlider->set_inverted(false); @@ -223,14 +242,14 @@ PdfImportDialog::PdfImportDialog(PDFDoc *doc, const gchar */*uri*/) _labelPrecisionComment->set_selectable(false); hbox6->pack_start(*_fallbackPrecisionSlider, Gtk::PACK_SHRINK, 4); hbox6->pack_start(*_labelPrecisionComment, Gtk::PACK_SHRINK, 0); - _labelText->set_alignment(0.5,0.5); - _labelText->set_padding(4,0); - _labelText->set_justify(Gtk::JUSTIFY_LEFT); - _labelText->set_line_wrap(false); - _labelText->set_use_markup(false); - _labelText->set_selectable(false); - hbox5->pack_start(*_labelText, Gtk::PACK_SHRINK, 0); - hbox5->pack_start(*_textHandlingCombo, Gtk::PACK_SHRINK, 0); + // _labelText->set_alignment(0.5,0.5); + // _labelText->set_padding(4,0); + // _labelText->set_justify(Gtk::JUSTIFY_LEFT); + // _labelText->set_line_wrap(false); + // _labelText->set_use_markup(false); + // _labelText->set_selectable(false); + // hbox5->pack_start(*_labelText, Gtk::PACK_SHRINK, 0); + // hbox5->pack_start(*_textHandlingCombo, Gtk::PACK_SHRINK, 0); _localFontsCheck->set_can_focus(); _localFontsCheck->set_relief(Gtk::RELIEF_NORMAL); _localFontsCheck->set_mode(true); @@ -240,14 +259,17 @@ PdfImportDialog::PdfImportDialog(PDFDoc *doc, const gchar */*uri*/) _embedImagesCheck->set_mode(true); _embedImagesCheck->set_active(true); #ifdef HAVE_POPPLER_CAIRO - vbox3->pack_start(*_importviaPopplerCheck, Gtk::PACK_SHRINK, 0); + vbox3->pack_start(*_importViaPoppler, Gtk::PACK_SHRINK, 0); + vbox3->pack_start(*_labelViaPoppler, Gtk::PACK_SHRINK, 0); + vbox3->pack_start(*_importViaInternal, Gtk::PACK_SHRINK, 0); + vbox3->pack_start(*_labelViaInternal, Gtk::PACK_SHRINK, 0); #endif + vbox3->pack_start(*_localFontsCheck, Gtk::PACK_SHRINK, 0); + vbox3->pack_start(*_embedImagesCheck, Gtk::PACK_SHRINK, 0); vbox3->pack_start(*_labelPrecision, Gtk::PACK_SHRINK, 0); vbox3->pack_start(*hbox6, Gtk::PACK_SHRINK, 0); vbox3->pack_start(*_labelPrecisionWarning, Gtk::PACK_SHRINK, 0); - vbox3->pack_start(*hbox5, Gtk::PACK_SHRINK, 4); - vbox3->pack_start(*_localFontsCheck, Gtk::PACK_SHRINK, 0); - vbox3->pack_start(*_embedImagesCheck, Gtk::PACK_SHRINK, 0); + // vbox3->pack_start(*hbox5, Gtk::PACK_SHRINK, 4); _importSettingsFrame->add(*vbox3); _importSettingsFrame->set_border_width(4); vbox1->pack_start(*_pageSettingsFrame, Gtk::PACK_EXPAND_PADDING, 0); @@ -273,36 +295,9 @@ PdfImportDialog::PdfImportDialog(PDFDoc *doc, const gchar */*uri*/) this->property_destroy_with_parent().set_value(false); this->add_action_widget(*cancelbutton, -6); this->add_action_widget(*okbutton, -5); - cancelbutton->show(); - okbutton->show(); - _labelSelect->show(); - _pageNumberSpin->show(); - _labelTotalPages->show(); - hbox2->show(); - _cropCheck->show(); - _cropTypeCombo->show(); - hbox3->show(); - vbox2->show(); - _pageSettingsFrame->show(); - _labelPrecision->show(); - _labelPrecisionWarning->show(); -#ifdef HAVE_POPPLER_CAIRO - _importviaPopplerCheck->show(); -#endif - _fallbackPrecisionSlider->show(); - _labelPrecisionComment->show(); - hbox6->show(); - _labelText->show(); - _textHandlingCombo->show(); - hbox5->show(); - _localFontsCheck->show(); - _embedImagesCheck->show(); - vbox3->show(); - _importSettingsFrame->show(); - vbox1->show(); - _previewArea->show(); - hbox1->show(); + this->show_all(); + // Connect signals #if WITH_GTKMM_3_0 _previewArea->signal_draw().connect(sigc::mem_fun(*this, &PdfImportDialog::_onDraw)); @@ -313,17 +308,28 @@ PdfImportDialog::PdfImportDialog(PDFDoc *doc, const gchar */*uri*/) _pageNumberSpin_adj->signal_value_changed().connect(sigc::mem_fun(*this, &PdfImportDialog::_onPageNumberChanged)); _cropCheck->signal_toggled().connect(sigc::mem_fun(*this, &PdfImportDialog::_onToggleCropping)); _fallbackPrecisionSlider_adj->signal_value_changed().connect(sigc::mem_fun(*this, &PdfImportDialog::_onPrecisionChanged)); +#ifdef HAVE_POPPLER_CAIRO + _importViaPoppler->signal_toggled().connect(sigc::mem_fun(*this, &PdfImportDialog::_onToggleImport)); +#endif _render_thumb = false; #ifdef HAVE_POPPLER_CAIRO _cairo_surface = NULL; _render_thumb = true; + // Create PopplerDocument - gchar *doc_uri = g_filename_to_uri(_pdf_doc->getFileName()->getCString(),NULL,NULL); - if (doc_uri) { - _poppler_doc = poppler_document_new_from_file(doc_uri, NULL, NULL); - g_free(doc_uri); + Glib::ustring filename = _pdf_doc->getFileName()->getCString(); + if (!Glib::path_is_absolute(filename)) { + filename = Glib::build_filename(Glib::get_current_dir(),filename); + } + Glib::ustring full_uri = Glib::filename_to_uri(filename); + + if (!full_uri.empty()) { + _poppler_doc = poppler_document_new_from_file(full_uri.c_str(), NULL, NULL); } + + // Set sensitivity of some widgets based on selected import type. + _onToggleImport(); #endif // Set default preview size @@ -373,11 +379,11 @@ int PdfImportDialog::getSelectedPage() { return _current_page; } -int PdfImportDialog::getImportMethod() { +bool PdfImportDialog::getImportMethod() { #ifdef HAVE_POPPLER_CAIRO - return (_importviaPopplerCheck->get_active()) ? 1 : 0; + return _importViaPoppler->get_active(); #else - return 0; + return false; #endif } @@ -413,7 +419,7 @@ void PdfImportDialog::getImportSettings(Inkscape::XML::Node *prefs) { prefs->setAttribute("embedImages", "0"); } #ifdef HAVE_POPPLER_CAIRO - if (_importviaPopplerCheck->get_active()) { + if (_importViaPoppler->get_active()) { prefs->setAttribute("importviapoppler", "1"); } else { prefs->setAttribute("importviapoppler", "0"); @@ -454,6 +460,23 @@ void PdfImportDialog::_onPageNumberChanged() { } #ifdef HAVE_POPPLER_CAIRO +void PdfImportDialog::_onToggleImport() { + if( _importViaPoppler->get_active() ) { + hbox3->set_sensitive(false); + _localFontsCheck->set_sensitive(false); + _embedImagesCheck->set_sensitive(false); + hbox6->set_sensitive(false); + } else { + hbox3->set_sensitive(); + _localFontsCheck->set_sensitive(); + _embedImagesCheck->set_sensitive(); + hbox6->set_sensitive(); + } +} +#endif + + +#ifdef HAVE_POPPLER_CAIRO /** * \brief Copies image data from a Cairo surface to a pixbuf * @@ -673,8 +696,13 @@ PdfInput::open(::Inkscape::Extension::Input * /*mod*/, const gchar * uri) { 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 + + + // PDFDoc is from poppler. PDFDoc is used for preview and for native import. + #ifndef WIN32 + // poppler does not use glib g_open. So on win32 we must use unicode call. code was copied from + // glib gstdio.c GooString *filename_goo = new GooString(uri); PDFDoc *pdf_doc = new PDFDoc(filename_goo, NULL, NULL, NULL); // TODO: Could ask for password //delete filename_goo; @@ -730,26 +758,20 @@ PdfInput::open(::Inkscape::Extension::Input * /*mod*/, const gchar * uri) { } } - // Get needed page - int page_num; - if (dlg) + // Get options + int page_num = 1; + bool is_importvia_poppler = false; + if (dlg) { page_num = dlg->getSelectedPage(); - else - page_num = 1; - Catalog *catalog = pdf_doc->getCatalog(); - Page *page = catalog->getPage(page_num); - - int is_importvia_poppler = 0; - if(dlg) - { #ifdef HAVE_POPPLER_CAIRO is_importvia_poppler = dlg->getImportMethod(); + // printf("PDF import via %s.\n", is_importvia_poppler ? "poppler" : "native"); #endif } SPDocument *doc = NULL; bool saved = false; - if(is_importvia_poppler == 0) + if(!is_importvia_poppler) { // native importer doc = SPDocument::createNewDoc(NULL, TRUE, TRUE); @@ -769,12 +791,14 @@ PdfInput::open(::Inkscape::Extension::Input * /*mod*/, const gchar * uri) { if (dlg) dlg->getImportSettings(prefs); - printf("pdf import via %s.", (is_importvia_poppler != 0) ? "poppler" : "native"); - // Apply crop settings PDFRectangle *clipToBox = NULL; double crop_setting; sp_repr_get_double(prefs, "cropTo", &crop_setting); + + Catalog *catalog = pdf_doc->getCatalog(); + Page *page = catalog->getPage(page_num); + if ( crop_setting >= 0.0 ) { // Do page clipping int crop_choice = (int)crop_setting; switch (crop_choice) { @@ -798,11 +822,11 @@ PdfInput::open(::Inkscape::Extension::Input * /*mod*/, const gchar * uri) { } } - // Create parser + // Create parser (extension/internal/pdfinput/pdf-parser.h) PdfParser *pdf_parser = new PdfParser(pdf_doc->getXRef(), builder, page_num-1, page->getRotate(), page->getResourceDict(), page->getCropBox(), clipToBox); - // Set up approximation precision for parser + // Set up approximation precision for parser. Used for convering Mesh Gradients into tiles. double color_delta; sp_repr_get_double(prefs, "approximationPrecision", &color_delta); if ( color_delta <= 0.0 ) { @@ -831,13 +855,20 @@ PdfInput::open(::Inkscape::Extension::Input * /*mod*/, const gchar * uri) { { #ifdef HAVE_POPPLER_CAIRO // the poppler import - gchar* filename_uri = g_filename_to_uri(uri, NULL, NULL); + + Glib::ustring full_path = uri; + if (!Glib::path_is_absolute(uri)) { + full_path = Glib::build_filename(Glib::get_current_dir(),uri); + } + Glib::ustring full_uri = Glib::filename_to_uri(full_path); + GError *error = NULL; - /// @todo handle passwort + /// @todo handle password /// @todo check if win32 unicode needs special attention - PopplerDocument* document = poppler_document_new_from_file(filename_uri, NULL, &error); + PopplerDocument* document = poppler_document_new_from_file(full_uri.c_str(), NULL, &error); if(error != NULL) { + std::cerr << "PDFInput::open: error opening document: " << full_uri << std::endl; g_error_free (error); } @@ -850,6 +881,13 @@ PdfInput::open(::Inkscape::Extension::Input * /*mod*/, const gchar * uri) { Glib::ustring output; cairo_surface_t* surface = cairo_svg_surface_create_for_stream(Inkscape::Extension::Internal::_write_ustring_cb, &output, width, height); + + // This magical function results in more fine-grain fallbacks. In particular, a mesh + // gradient won't necessarily result in the whole PDF being rasterized. Of course, SVG + // 1.2 never made it as a standard, but hey, we'll take what we can get. This trick was + // found by examining the 'pdftocairo' code. + cairo_svg_surface_restrict_to_version( surface, CAIRO_SVG_VERSION_1_2 ); + cairo_t* cr = cairo_create(surface); poppler_page_render_for_printing(page, cr); @@ -867,13 +905,10 @@ PdfInput::open(::Inkscape::Extension::Input * /*mod*/, const gchar * uri) { } else { - doc = SPDocument::createNewDoc(NULL, TRUE, TRUE); // fallback create empthy document + doc = SPDocument::createNewDoc(NULL, TRUE, TRUE); // fallback create empty document } saved = DocumentUndo::getUndoSensitive(doc); DocumentUndo::setUndoSensitive(doc, false); // No need to undo in this temporary document - - // Cleanup - g_free(filename_uri); #endif } diff --git a/src/extension/internal/pdfinput/pdf-input.h b/src/extension/internal/pdfinput/pdf-input.h index d57c3e993..6e36603c3 100644 --- a/src/extension/internal/pdfinput/pdf-input.h +++ b/src/extension/internal/pdfinput/pdf-input.h @@ -16,10 +16,6 @@ #ifdef HAVE_POPPLER -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include <gtkmm/dialog.h> #include "../../implementation/implementation.h" @@ -48,6 +44,7 @@ namespace Gtk { #else class HScale; #endif + class RadioButton; class VBox; class Label; } @@ -75,7 +72,7 @@ public: bool showDialog(); int getSelectedPage(); - int getImportMethod(); + bool getImportMethod(); void getImportSettings(Inkscape::XML::Node *prefs); private: @@ -90,7 +87,10 @@ private: void _onPageNumberChanged(); void _onToggleCropping(); void _onPrecisionChanged(); - +#ifdef HAVE_POPPLER_CAIRO + void _onToggleImport(); +#endif + class Gtk::Button * cancelbutton; class Gtk::Button * okbutton; class Gtk::Label * _labelSelect; @@ -105,7 +105,10 @@ private: class Gtk::Label * _labelPrecision; class Gtk::Label * _labelPrecisionWarning; #ifdef HAVE_POPPLER_CAIRO - class Gtk::CheckButton * _importviaPopplerCheck; // using poppler_cairo for importing + class Gtk::RadioButton * _importViaPoppler; // Use poppler_cairo importing + class Gtk::Label * _labelViaPoppler; + class Gtk::RadioButton * _importViaInternal; // Use native (poppler based) importing + class Gtk::Label * _labelViaInternal; #endif #if WITH_GTKMM_3_0 class Gtk::Scale * _fallbackPrecisionSlider; diff --git a/src/extension/internal/pdfinput/pdf-parser.cpp b/src/extension/internal/pdfinput/pdf-parser.cpp index cc0b38515..836c34c32 100644 --- a/src/extension/internal/pdfinput/pdf-parser.cpp +++ b/src/extension/internal/pdfinput/pdf-parser.cpp @@ -2780,12 +2780,14 @@ void PdfParser::doImage(Object * /*ref*/, Stream *str, GBool inlineImg) Dict *dict; int width, height; int bits; + GBool interpolate; StreamColorSpaceMode csMode; GBool mask; GBool invert; Object maskObj, smaskObj; GBool haveColorKeyMask, haveExplicitMask, haveSoftMask; GBool maskInvert; + GBool maskInterpolate; Object obj1, obj2; // get info from the stream @@ -2828,6 +2830,19 @@ void PdfParser::doImage(Object * /*ref*/, Stream *str, GBool inlineImg) } obj1.free(); + // image interpolation + dict->lookup("Interpolate", &obj1); + if (obj1.isNull()) { + obj1.free(); + dict->lookup("I", &obj1); + } + if (obj1.isBool()) + interpolate = obj1.getBool(); + else + interpolate = gFalse; + obj1.free(); + maskInterpolate = gFalse; + // image or mask? dict->lookup(const_cast<char*>("ImageMask"), &obj1); if (obj1.isNull()) { @@ -2884,7 +2899,7 @@ void PdfParser::doImage(Object * /*ref*/, Stream *str, GBool inlineImg) obj1.free(); // draw it - builder->addImageMask(state, str, width, height, invert); + builder->addImageMask(state, str, width, height, invert, interpolate); } else { // get color space and color map @@ -2986,6 +3001,16 @@ void PdfParser::doImage(Object * /*ref*/, Stream *str, GBool inlineImg) } int maskBits = obj1.getInt(); obj1.free(); + maskDict->lookup("Interpolate", &obj1); + if (obj1.isNull()) { + obj1.free(); + maskDict->lookup("I", &obj1); + } + if (obj1.isBool()) + maskInterpolate = obj1.getBool(); + else + maskInterpolate = gFalse; + obj1.free(); maskDict->lookup(const_cast<char*>("ColorSpace"), &obj1); if (obj1.isNull()) { obj1.free(); @@ -3071,6 +3096,16 @@ void PdfParser::doImage(Object * /*ref*/, Stream *str, GBool inlineImg) goto err2; } obj1.free(); + maskDict->lookup("Interpolate", &obj1); + if (obj1.isNull()) { + obj1.free(); + maskDict->lookup("I", &obj1); + } + if (obj1.isBool()) + maskInterpolate = obj1.getBool(); + else + maskInterpolate = gFalse; + obj1.free(); maskInvert = gFalse; maskDict->lookup(const_cast<char*>("Decode"), &obj1); if (obj1.isNull()) { @@ -3092,14 +3127,14 @@ void PdfParser::doImage(Object * /*ref*/, Stream *str, GBool inlineImg) // draw it if (haveSoftMask) { - builder->addSoftMaskedImage(state, str, width, height, colorMap, - maskStr, maskWidth, maskHeight, maskColorMap); + builder->addSoftMaskedImage(state, str, width, height, colorMap, interpolate, + maskStr, maskWidth, maskHeight, maskColorMap, maskInterpolate); delete maskColorMap; } else if (haveExplicitMask) { - builder->addMaskedImage(state, str, width, height, colorMap, - maskStr, maskWidth, maskHeight, maskInvert); + builder->addMaskedImage(state, str, width, height, colorMap, interpolate, + maskStr, maskWidth, maskHeight, maskInvert, maskInterpolate); } else { - builder->addImage(state, str, width, height, colorMap, + builder->addImage(state, str, width, height, colorMap, interpolate, haveColorKeyMask ? maskColors : static_cast<int *>(NULL)); } delete colorMap; diff --git a/src/extension/internal/pdfinput/svg-builder.cpp b/src/extension/internal/pdfinput/svg-builder.cpp index a3abb4045..58e2030d9 100644 --- a/src/extension/internal/pdfinput/svg-builder.cpp +++ b/src/extension/internal/pdfinput/svg-builder.cpp @@ -1485,7 +1485,9 @@ void png_flush_base64stream(png_structp png_ptr) * */ Inkscape::XML::Node *SvgBuilder::_createImage(Stream *str, int width, int height, - GfxImageColorMap *color_map, int *mask_colors, bool alpha_only, bool invert_alpha) { + GfxImageColorMap *color_map, bool interpolate, + int *mask_colors, bool alpha_only, + bool invert_alpha) { // Create PNG write struct png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); @@ -1655,6 +1657,13 @@ Inkscape::XML::Node *SvgBuilder::_createImage(Stream *str, int width, int height Inkscape::XML::Node *image_node = _xml_doc->createElement("svg:image"); sp_repr_set_svg_double(image_node, "width", 1); sp_repr_set_svg_double(image_node, "height", 1); + if( !interpolate ) { + SPCSSAttr *css = sp_repr_css_attr_new(); + // This should be changed after CSS4 Images widely supported. + sp_repr_css_set_property(css, "image-rendering", "optimizeSpeed"); + sp_repr_css_change(image_node, css, "style"); + sp_repr_css_attr_unref(css); + } // PS/PDF images are placed via a transformation matrix, no preserveAspectRatio used image_node->setAttribute("preserveAspectRatio", "none"); @@ -1715,9 +1724,9 @@ Inkscape::XML::Node *SvgBuilder::_createMask(double width, double height) { } void SvgBuilder::addImage(GfxState * /*state*/, Stream *str, int width, int height, - GfxImageColorMap *color_map, int *mask_colors) { + GfxImageColorMap *color_map, bool interpolate, int *mask_colors) { - Inkscape::XML::Node *image_node = _createImage(str, width, height, color_map, mask_colors); + Inkscape::XML::Node *image_node = _createImage(str, width, height, color_map, interpolate, mask_colors); if (image_node) { _container->appendChild(image_node); Inkscape::GC::release(image_node); @@ -1725,7 +1734,7 @@ void SvgBuilder::addImage(GfxState * /*state*/, Stream *str, int width, int heig } void SvgBuilder::addImageMask(GfxState *state, Stream *str, int width, int height, - bool invert) { + bool invert, bool interpolate) { // Create a rectangle Inkscape::XML::Node *rect = _xml_doc->createElement("svg:rect"); @@ -1742,7 +1751,8 @@ void SvgBuilder::addImageMask(GfxState *state, Stream *str, int width, int heigh // Scaling 1x1 surfaces might not work so skip setting a mask with this size if ( width > 1 || height > 1 ) { - Inkscape::XML::Node *mask_image_node = _createImage(str, width, height, NULL, NULL, true, invert); + Inkscape::XML::Node *mask_image_node = + _createImage(str, width, height, NULL, interpolate, NULL, true, invert); if (mask_image_node) { // Create the mask Inkscape::XML::Node *mask_node = _createMask(1.0, 1.0); @@ -1762,13 +1772,13 @@ void SvgBuilder::addImageMask(GfxState *state, Stream *str, int width, int heigh } void SvgBuilder::addMaskedImage(GfxState * /*state*/, Stream *str, int width, int height, - GfxImageColorMap *color_map, + GfxImageColorMap *color_map, bool interpolate, Stream *mask_str, int mask_width, int mask_height, - bool invert_mask) { + bool invert_mask, bool mask_interpolate) { Inkscape::XML::Node *mask_image_node = _createImage(mask_str, mask_width, mask_height, - NULL, NULL, true, invert_mask); - Inkscape::XML::Node *image_node = _createImage(str, width, height, color_map, NULL); + NULL, mask_interpolate, NULL, true, invert_mask); + Inkscape::XML::Node *image_node = _createImage(str, width, height, color_map, interpolate, NULL); if ( mask_image_node && image_node ) { // Create mask for the image Inkscape::XML::Node *mask_node = _createMask(1.0, 1.0); @@ -1795,13 +1805,13 @@ void SvgBuilder::addMaskedImage(GfxState * /*state*/, Stream *str, int width, in } void SvgBuilder::addSoftMaskedImage(GfxState * /*state*/, Stream *str, int width, int height, - GfxImageColorMap *color_map, + GfxImageColorMap *color_map, bool interpolate, Stream *mask_str, int mask_width, int mask_height, - GfxImageColorMap *mask_color_map) { + GfxImageColorMap *mask_color_map, bool mask_interpolate) { Inkscape::XML::Node *mask_image_node = _createImage(mask_str, mask_width, mask_height, - mask_color_map, NULL, true); - Inkscape::XML::Node *image_node = _createImage(str, width, height, color_map, NULL); + mask_color_map, mask_interpolate, NULL, true); + Inkscape::XML::Node *image_node = _createImage(str, width, height, color_map, interpolate, NULL); if ( mask_image_node && image_node ) { // Create mask for the image Inkscape::XML::Node *mask_node = _createMask(1.0, 1.0); diff --git a/src/extension/internal/pdfinput/svg-builder.h b/src/extension/internal/pdfinput/svg-builder.h index f1ce02cf0..ad15c9c06 100644 --- a/src/extension/internal/pdfinput/svg-builder.h +++ b/src/extension/internal/pdfinput/svg-builder.h @@ -112,17 +112,17 @@ public: // Image handling void addImage(GfxState *state, Stream *str, int width, int height, - GfxImageColorMap *color_map, int *mask_colors); + GfxImageColorMap *color_map, bool interpolate, int *mask_colors); void addImageMask(GfxState *state, Stream *str, int width, int height, - bool invert); + bool invert, bool interpolate); void addMaskedImage(GfxState *state, Stream *str, int width, int height, - GfxImageColorMap *color_map, + GfxImageColorMap *color_map, bool interpolate, Stream *mask_str, int mask_width, int mask_height, - bool invert_mask); + bool invert_mask, bool mask_interpolate); void addSoftMaskedImage(GfxState *state, Stream *str, int width, int height, - GfxImageColorMap *color_map, + GfxImageColorMap *color_map, bool interpolate, Stream *mask_str, int mask_width, int mask_height, - GfxImageColorMap *mask_color_map); + GfxImageColorMap *mask_color_map, bool mask_interpolate); // Transparency group and soft mask handling void pushTransparencyGroup(GfxState *state, double *bbox, @@ -180,8 +180,9 @@ private: bool is_stroke=false); // Image/mask creation Inkscape::XML::Node *_createImage(Stream *str, int width, int height, - GfxImageColorMap *color_map, int *mask_colors, - bool alpha_only=false, bool invert_alpha=false); + GfxImageColorMap *color_map, bool interpolate, + int *mask_colors, bool alpha_only=false, + bool invert_alpha=false); Inkscape::XML::Node *_createMask(double width, double height); // Style setting SPCSSAttr *_setStyle(GfxState *state, bool fill, bool stroke, bool even_odd=false); diff --git a/src/extension/internal/pov-out.cpp b/src/extension/internal/pov-out.cpp index 3d149928f..bd2168b68 100644 --- a/src/extension/internal/pov-out.cpp +++ b/src/extension/internal/pov-out.cpp @@ -30,8 +30,7 @@ #include <extension/system.h> #include <2geom/pathvector.h> #include <2geom/rect.h> -#include <2geom/bezier-curve.h> -#include <2geom/hvlinesegment.h> +#include <2geom/curves.h> #include "helper/geom.h" #include "helper/geom-curves.h" #include <io/sys.h> @@ -388,7 +387,7 @@ bool PovOutput::doCurve(SPItem *item, const String &id) } else if(Geom::CubicBezier const *cubic = dynamic_cast<Geom::CubicBezier const*>(&*cit)) { - std::vector<Geom::Point> points = cubic->points(); + std::vector<Geom::Point> points = cubic->controlPoints(); Geom::Point p0 = points[0]; Geom::Point p1 = points[1]; Geom::Point p2 = points[2]; diff --git a/src/extension/internal/text_reassemble.c b/src/extension/internal/text_reassemble.c index d3aafef12..fa983b83d 100644 --- a/src/extension/internal/text_reassemble.c +++ b/src/extension/internal/text_reassemble.c @@ -67,8 +67,8 @@ Optional compiler switches for development: File: text_reassemble.c -Version: 0.0.16 -Date: 25-FEB-2015 +Version: 0.0.17 +Date: 21-MAY-2015 Author: David Mathog, Biology Division, Caltech email: mathog@caltech.edu Copyright: 2015 David Mathog and California Institute of Technology (Caltech) @@ -2204,10 +2204,10 @@ void TR_layout_2_svg(TR_INFO *tri){ sprintf(obuf,"text-decoration:"); /* multiple text decoration styles may be set */ utmp = tsp->decoration & TXTDECOR_TMASK; - if(utmp & TXTDECOR_UNDER ){ strcat(obuf,"underline"); } - if(utmp & TXTDECOR_OVER ){ strcat(obuf,"overline"); } - if(utmp & TXTDECOR_BLINK ){ strcat(obuf,"blink"); } - if(utmp & TXTDECOR_STRIKE){ strcat(obuf,"line-through");} + if(utmp & TXTDECOR_UNDER ){ strcat(obuf," underline"); } + if(utmp & TXTDECOR_OVER ){ strcat(obuf," overline"); } + if(utmp & TXTDECOR_BLINK ){ strcat(obuf," blink"); } + if(utmp & TXTDECOR_STRIKE){ strcat(obuf," line-through");} if(*obuf){ /* only a single text decoration line type may be set */ switch(tsp->decoration & TXTDECOR_LMASK){ diff --git a/src/extension/internal/vsd-input.h b/src/extension/internal/vsd-input.h index 3414e0ec9..acc52debf 100644 --- a/src/extension/internal/vsd-input.h +++ b/src/extension/internal/vsd-input.h @@ -19,10 +19,6 @@ #ifdef WITH_LIBVISIO -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include <gtkmm/dialog.h> #include "../implementation/implementation.h" diff --git a/src/extension/internal/wmf-inout.cpp b/src/extension/internal/wmf-inout.cpp index 503a93418..d17180d91 100644 --- a/src/extension/internal/wmf-inout.cpp +++ b/src/extension/internal/wmf-inout.cpp @@ -95,7 +95,6 @@ Wmf::print_document_to_file(SPDocument *doc, const gchar *filename) SPPrintContext context; const gchar *oldconst; gchar *oldoutput; - unsigned int ret; doc->ensureUpToDate(); @@ -114,13 +113,12 @@ Wmf::print_document_to_file(SPDocument *doc, const gchar *filename) mod->root = mod->base->invoke_show(drawing, mod->dkey, SP_ITEM_SHOW_DISPLAY); drawing.setRoot(mod->root); /* Print document */ - ret = mod->begin(doc); - if (ret) { + if (mod->begin(doc)) { g_free(oldoutput); throw Inkscape::Extension::Output::save_failed(); } mod->base->invoke_print(&context); - ret = mod->finish(); + mod->finish(); /* Release arena */ mod->base->invoke_hide(mod->dkey); mod->base = NULL; @@ -451,7 +449,8 @@ uint32_t Wmf::add_dib_image(PWMF_CALLBACK_DATA d, const char *dib, uint32_t iUsa char *rgba_px = NULL; // RGBA pixels const char *px = NULL; // DIB pixels const U_RGBQUAD *ct = NULL; // DIB color table - int32_t width, height, colortype, numCt, invert; // if needed these values will be set by wget_DIB_params + uint32_t numCt; + int32_t width, height, colortype, invert; // if needed these values will be set by wget_DIB_params if(iUsage == U_DIB_RGB_COLORS){ // next call returns pointers and values, but allocates no memory dibparams = wget_DIB_params(dib, &px, &ct, &numCt, &width, &height, &colortype, &invert); @@ -1320,7 +1319,8 @@ void Wmf::common_dib_to_image(PWMF_CALLBACK_DATA d, const char *dib, char *sub_px = NULL; // RGBA pixels, subarray const char *px = NULL; // DIB pixels const U_RGBQUAD *ct = NULL; // color table - int32_t width, height, colortype, numCt, invert; // if needed these values will be set in wget_DIB_params + uint32_t numCt; + int32_t width, height, colortype, invert; // if needed these values will be set in wget_DIB_params if(iUsage == U_DIB_RGB_COLORS){ // next call returns pointers and values, but allocates no memory dibparams = wget_DIB_params(dib, &px, &ct, &numCt, &width, &height, &colortype, &invert); @@ -1497,6 +1497,7 @@ int Wmf::myMetaFileProc(const char *contents, unsigned int length, PWMF_CALLBACK uint32_t off=0; uint32_t wmr_mask; int OK =1; + int file_status=1; TCHUNK_SPECS tsp; uint8_t iType; int nSize; // size of the current record, in bytes, or an error value if <=0 @@ -1562,7 +1563,9 @@ int Wmf::myMetaFileProc(const char *contents, unsigned int length, PWMF_CALLBACK U_WMRHEADER Header; off = 0; nSize = wmfheader_get(contents, blimit, &Placeable, &Header); - if(!nSize)return(0); + if (!nSize) { + return(0); + } if(!Header.nObjects){ Header.nObjects = 256; }// there _may_ be WMF files with no objects, more likely it is corrupt. Try to use it anyway. d->n_obj = Header.nObjects; d->wmf_obj = new WMF_OBJECT[d->n_obj]; @@ -1602,7 +1605,10 @@ int Wmf::myMetaFileProc(const char *contents, unsigned int length, PWMF_CALLBACK else { off += nSize; } - } + } + else { + return(0); + } } off=0; nSize = hold_nSize; @@ -1665,27 +1671,32 @@ int Wmf::myMetaFileProc(const char *contents, unsigned int length, PWMF_CALLBACK while(OK){ - if(off>=length)return(0); //normally should exit from while after WMREOF sets OK to false. + if (off>=length) { + return(0); //normally should exit from while after WMREOF sets OK to false. + } contents += nSize; // pointer to the start of the next record off += nSize; // offset from beginning of buffer to the start of the next record - SVGOStringStream tmp_path; - SVGOStringStream tmp_str; - - /* Check that the current record size is OK, abort if not. - Pointer math might wrap, so check both sides of the range. - Some of the records will reset this with the same value,others will not - return a value at this time. */ + /* Currently this is a weaker check than for EMF, it only checks the size of the constant part + of the record */ nSize = U_WMRRECSAFE_get(contents, blimit); - if(!nSize)break; + if(!nSize) { + file_status = 0; + break; + } iType = *(uint8_t *)(contents + offsetof(U_METARECORD, iType ) ); + wmr_mask = U_wmr_properties(iType); + if (wmr_mask == U_WMR_INVALID) { + file_status = 0; + break; + } // Uncomment the following to track down toxic records // std::cout << "record type: " << (int) iType << " name " << U_wmr_names(iType) << " length: " << nSize << " offset: " << off <<std::endl; - wmr_mask = U_wmr_properties(iType); - if(wmr_mask == U_WMR_INVALID){ throw "Inkscape fatal programming error at U_wmr_properties"; } + SVGOStringStream tmp_path; + SVGOStringStream tmp_str; /* Uncomment the following to track down text problems */ //std::cout << "tri->dirty:"<< d->tri->dirty << " wmr_mask: " << std::hex << wmr_mask << std::dec << std::endl; @@ -3026,14 +3037,17 @@ std::cout << "BEFORE DRAW" // When testing, uncomment the following to place a comment for each processed WMR record in the SVG // d->outsvg += dbg_str.str().c_str(); d->path += tmp_path.str().c_str(); - if(!nSize){ OK=0; std::cout << "nSize == 0, oops!!!" << std::endl; } // There was some problem with this record, it is not safe to continue + if(!nSize){ // There was some problem with the processing of this record, it is not safe to continue + file_status = 0; + break; + } - } //end of while + } //end of while on OK // When testing, uncomment the following to show the final SVG derived from the WMF // std::cout << d->outsvg << std::endl; (void) U_wmr_properties(U_WMR_INVALID); // force the release of the lookup table memory, returned value is irrelevant - return 1; + return(file_status); } void Wmf::free_wmf_strings(WMF_STRINGS name){ @@ -3049,7 +3063,14 @@ SPDocument * Wmf::open( Inkscape::Extension::Input * /*mod*/, const gchar *uri ) { + if (uri == NULL) { + return NULL; + } + WMF_CALLBACK_DATA d; + + d.n_obj = 0; //these might not be set otherwise if the input file is corrupt + d.wmf_obj=NULL; // Default font, WMF spec says device can pick whatever it wants. // WMF files that do not specify a font are unlikely to look very good! @@ -3064,12 +3085,12 @@ Wmf::open( Inkscape::Extension::Input * /*mod*/, const gchar *uri ) d.dc[0].style.stroke_dasharray.set = 0; d.dc[0].style.stroke_linecap.computed = 2; // U_PS_ENDCAP_SQUARE; d.dc[0].style.stroke_linejoin.computed = 0; // U_PS_JOIN_MITER; - d.dc[0].style.stroke_width.value = 1.0; // will be reset to something reasonable once WMF draying size is known + d.dc[0].style.stroke_width.value = 1.0; // will be reset to something reasonable once WMF drawing size is known d.dc[0].style.stroke.value.color.set( 0, 0, 0 ); + d.dc[0].stroke_set = true; - if (uri == NULL) { - return NULL; - } + // Default brush is none - no fill. WMF files that do not specify a brush are unlikely to look very good! + d.dc[0].fill_set = false; d.dc[0].font_name = strdup("Arial"); // Default font, set only on lowest level, it copies up from there WMF spec says device can pick whatever it wants @@ -3095,12 +3116,15 @@ Wmf::open( Inkscape::Extension::Input * /*mod*/, const gchar *uri ) FT_LOAD_NO_SCALE | FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP, FT_KERNING_UNSCALED); - (void) myMetaFileProc(contents,length, &d); + int good = myMetaFileProc(contents,length, &d); free(contents); // std::cout << "SVG Output: " << std::endl << d.outsvg << std::endl; - SPDocument *doc = SPDocument::createNewDocFromMem(d.outsvg.c_str(), strlen(d.outsvg.c_str()), TRUE); + SPDocument *doc = NULL; + if (good) { + doc = SPDocument::createNewDocFromMem(d.outsvg.c_str(), strlen(d.outsvg.c_str()), TRUE); + } free_wmf_strings(d.hatches); free_wmf_strings(d.images); diff --git a/src/extension/internal/wmf-print.cpp b/src/extension/internal/wmf-print.cpp index 567f9f366..431053085 100644 --- a/src/extension/internal/wmf-print.cpp +++ b/src/extension/internal/wmf-print.cpp @@ -29,14 +29,13 @@ #endif -#include "2geom/sbasis-to-bezier.h" -#include "2geom/svg-elliptical-arc.h" - -#include "2geom/path.h" -#include "2geom/pathvector.h" -#include "2geom/rect.h" -#include "2geom/bezier-curve.h" -#include "2geom/hvlinesegment.h" +#include <2geom/sbasis-to-bezier.h> +#include <2geom/elliptical-arc.h> + +#include <2geom/path.h> +#include <2geom/pathvector.h> +#include <2geom/rect.h> +#include <2geom/curves.h> #include "helper/geom.h" #include "helper/geom-curves.h" #include "sp-item.h" @@ -59,8 +58,9 @@ #include "display/cairo-utils.h" #include "splivarot.h" // pieces for union on shapes -#include "2geom/svg-path-parser.h" // to get from SVG text to Geom::Path +#include <2geom/svg-path-parser.h> // to get from SVG text to Geom::Path #include "display/canvas-bpath.h" // for SPWindRule +#include "display/cairo-utils.h" // for Inkscape::Pixbuf::PF_CAIRO #include "wmf-print.h" @@ -379,8 +379,8 @@ int PrintWmf::create_brush(SPStyle const *style, U_COLORREF *fcolor) } else if (SP_IS_PATTERN(SP_STYLE_FILL_SERVER(style))) { // must be paint-server SPPaintServer *paintserver = style->fill.value.href->getObject(); SPPattern *pat = SP_PATTERN(paintserver); - double dwidth = pattern_width(pat); - double dheight = pattern_height(pat); + double dwidth = pat->width(); + double dheight = pat->height(); width = dwidth; height = dheight; brush_classify(pat, 0, &pixbuf, &hatchType, &hatchColor, &bkColor); @@ -471,9 +471,8 @@ int PrintWmf::create_brush(SPStyle const *style, U_COLORREF *fcolor) rgba_px = (char *) pixbuf->pixels(); // Do NOT free this!!! colortype = U_BCBM_COLOR32; (void) RGBA_to_DIB(&px, &cbPx, &ct, &numCt, rgba_px, width, height, width * 4, colortype, 0, 1); - // Not sure why the next swap is needed because the preceding does it, and the code is identical - // to that in stretchdibits_set, which does not need this. - swapRBinRGBA(px, width * height); + // pixbuf can be either PF_CAIRO or PF_GDK, and these have R and B bytes swapped + if (pixbuf->pixelFormat() == Inkscape::Pixbuf::PF_CAIRO) { swapRBinRGBA(px, width * height); } Bmih = bitmapinfoheader_set(width, height, 1, colortype, U_BI_RGB, 0, PXPERMETER, PXPERMETER, numCt, 0); Bmi = bitmapinfo_set(Bmih, ct); rec = wcreatedibpatternbrush_srcdib_set(&brush, wht, U_DIB_RGB_COLORS, Bmi, cbPx, px); @@ -984,7 +983,7 @@ bool PrintWmf::print_simple_shape(Geom::PathVector const &pathv, const Geom::Aff lpPoints[i].y = y1; i = i + 1; } else if (Geom::CubicBezier const *cubic = dynamic_cast<Geom::CubicBezier const *>(&*cit)) { - std::vector<Geom::Point> points = cubic->points(); + std::vector<Geom::Point> points = cubic->controlPoints(); //Geom::Point p0 = points[0]; Geom::Point p1 = points[1]; Geom::Point p2 = points[2]; diff --git a/src/extension/param/bool.cpp b/src/extension/param/bool.cpp index 548dec4fa..de9b1c586 100644 --- a/src/extension/param/bool.cpp +++ b/src/extension/param/bool.cpp @@ -10,10 +10,6 @@ # include "config.h" #endif -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include <gtkmm/adjustment.h> #include <gtkmm/box.h> #include <gtkmm/spinbutton.h> diff --git a/src/extension/param/color.cpp b/src/extension/param/color.cpp index 5bd70359f..0b58c5011 100644 --- a/src/extension/param/color.cpp +++ b/src/extension/param/color.cpp @@ -14,10 +14,6 @@ #include <iostream> #include <sstream> -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include <gtkmm/adjustment.h> #include <gtkmm/box.h> #include <gtkmm/spinbutton.h> @@ -28,41 +24,37 @@ #include "color.h" #include <color.h> -#include "widgets/sp-color-selector.h" -#include "widgets/sp-color-notebook.h" +#include "ui/widget/color-notebook.h" #include "preferences.h" - namespace Inkscape { namespace Extension { -void sp_color_param_changed(SPColorSelector *csel, GObject *cp); - - ParamColor::~ParamColor(void) { - + _color_changed.disconnect(); } guint32 ParamColor::set( guint32 in, SPDocument * /*doc*/, Inkscape::XML::Node * /*node*/ ) { - _value = in; + _color_changed.block(true); + _color.setValue(in); + _color_changed.block(false); gchar * prefname = this->pref_name(); std::string value; string(value); - + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); prefs->setString(extension_pref_root + prefname, value); g_free(prefname); - return _value; + return in; } -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) +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) { const char * defaulthex = NULL; if (xml->firstChild() != NULL) @@ -76,51 +68,48 @@ ParamColor::ParamColor(const gchar *name, const gchar *guitext, const gchar *des if (!paramval.empty()) defaulthex = paramval.data(); - if (defaulthex) - _value = atoi(defaulthex); + if (defaulthex) { + _color.setValue(atoi(defaulthex)); + } + _color_changed = _color.signal_changed.connect(sigc::mem_fun(this, &ParamColor::_onColorChanged)); + } void ParamColor::string(std::string &string) const { char str[16]; - sprintf(str, "%i", _value); + snprintf(str, 16, "%i", _color.value()); string += str; } Gtk::Widget *ParamColor::get_widget( SPDocument * /*doc*/, Inkscape::XML::Node * /*node*/, sigc::signal<void> * changeSignal ) { - if (_gui_hidden) return NULL; + using Inkscape::UI::Widget::ColorNotebook; - _changeSignal = new sigc::signal<void>(*changeSignal); - Gtk::HBox * hbox = Gtk::manage(new Gtk::HBox(false, 4)); - SPColorSelector* spColorSelector = (SPColorSelector*)sp_color_selector_new(SP_TYPE_COLOR_NOTEBOOK); + if (_gui_hidden) return NULL; - ColorSelector* colorSelector = spColorSelector->base; - if (_value < 1) { - _value = 0xFF000000; + if (changeSignal) { + _changeSignal = new sigc::signal<void>(*changeSignal); } - SPColor *color = new SPColor( _value ); - float alpha = (_value & 0xff) / 255.0F; - colorSelector->setColorAlpha(*color, alpha); - hbox->pack_start (*Glib::wrap(&spColorSelector->vbox), true, true, 0); - g_signal_connect(G_OBJECT(spColorSelector), "changed", G_CALLBACK(sp_color_param_changed), (void*)this); + if (_color.value() < 1) { + _color_changed.block(true); + _color.setValue(0xFF000000); + _color_changed.block(false); + } - gtk_widget_show(GTK_WIDGET(spColorSelector)); + Gtk::HBox *hbox = Gtk::manage(new Gtk::HBox(false, 4)); + Gtk::Widget *selector = Gtk::manage(new ColorNotebook(_color)); + hbox->pack_start (*selector, true, true, 0); + selector->show(); hbox->show(); - - return dynamic_cast<Gtk::Widget *>(hbox); + return hbox; } -void sp_color_param_changed(SPColorSelector *csel, GObject *obj) +void ParamColor::_onColorChanged() { - const SPColor color = csel->base->getColor(); - float alpha = csel->base->getAlpha(); - - ParamColor* ptr = reinterpret_cast<ParamColor*>(obj); - ptr->set(color.toRGBA32( alpha ), NULL, NULL); - - ptr->_changeSignal->emit(); + if (_changeSignal) + _changeSignal->emit(); } }; /* namespace Extension */ diff --git a/src/extension/param/color.h b/src/extension/param/color.h index 9894965a9..ed2e57ceb 100644 --- a/src/extension/param/color.h +++ b/src/extension/param/color.h @@ -9,6 +9,7 @@ */ #include "parameter.h" +#include "ui/selected-color.h" class SPDocument; @@ -25,14 +26,17 @@ namespace Extension { class ParamColor : public Parameter { private: - guint32 _value; + void _onColorChanged(); + + Inkscape::UI::SelectedColor _color; + sigc::connection _color_changed; public: 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); virtual ~ParamColor(void); /** Returns \c _value, with a \i const to protect it. */ - guint32 get( SPDocument const * /*doc*/, Inkscape::XML::Node const * /*node*/ ) const { return _value; } + guint32 get( SPDocument const * /*doc*/, Inkscape::XML::Node const * /*node*/ ) const { return _color.value(); } guint32 set (guint32 in, SPDocument * doc, Inkscape::XML::Node * node); @@ -44,6 +48,7 @@ public: virtual void string (std::string &string) const; sigc::signal<void> * _changeSignal; + }; // class ParamColor } // namespace Extension diff --git a/src/extension/param/description.cpp b/src/extension/param/description.cpp index 95ed04afc..326e75e4a 100644 --- a/src/extension/param/description.cpp +++ b/src/extension/param/description.cpp @@ -13,10 +13,6 @@ # include "config.h" #endif -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include <gtkmm/box.h> #include <gtkmm/label.h> #include <glibmm/i18n.h> diff --git a/src/extension/param/enum.cpp b/src/extension/param/enum.cpp index 74b2a75ad..8bc0fbda7 100644 --- a/src/extension/param/enum.cpp +++ b/src/extension/param/enum.cpp @@ -18,10 +18,6 @@ # include "config.h" #endif -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include <gtkmm/box.h> #include <gtkmm/comboboxtext.h> #include <glibmm/i18n.h> diff --git a/src/extension/param/float.cpp b/src/extension/param/float.cpp index 464938f03..81508f6c0 100644 --- a/src/extension/param/float.cpp +++ b/src/extension/param/float.cpp @@ -10,10 +10,6 @@ # include "config.h" #endif -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include <gtkmm/adjustment.h> #include <gtkmm/box.h> #include <gtkmm/scale.h> diff --git a/src/extension/param/int.cpp b/src/extension/param/int.cpp index 819c75693..c286018fd 100644 --- a/src/extension/param/int.cpp +++ b/src/extension/param/int.cpp @@ -10,10 +10,6 @@ # include "config.h" #endif -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include <gtkmm/adjustment.h> #include <gtkmm/box.h> #include <gtkmm/scale.h> diff --git a/src/extension/param/notebook.cpp b/src/extension/param/notebook.cpp index 9ec31ca6b..20c8e8481 100644 --- a/src/extension/param/notebook.cpp +++ b/src/extension/param/notebook.cpp @@ -16,10 +16,6 @@ # include "config.h" #endif -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include <gtkmm/adjustment.h> #include <gtkmm/box.h> #include <gtkmm/spinbutton.h> diff --git a/src/extension/param/parameter.cpp b/src/extension/param/parameter.cpp index 202b8110f..8c99ee55d 100644 --- a/src/extension/param/parameter.cpp +++ b/src/extension/param/parameter.cpp @@ -15,6 +15,10 @@ # include "config.h" #endif +#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H +#include <glibmm/threads.h> +#endif + #ifdef linux // does the dollar sign need escaping when passed as string parameter? # define ESCAPE_DOLLAR_COMMANDLINE #endif @@ -26,8 +30,7 @@ #include "document-private.h" #include "sp-object.h" #include <color.h> -#include "widgets/sp-color-selector.h" -#include "widgets/sp-color-notebook.h" +#include "ui/widget/color-notebook.h" #include "parameter.h" #include "bool.h" diff --git a/src/extension/param/radiobutton.cpp b/src/extension/param/radiobutton.cpp index 740acf1d1..8181c6f51 100644 --- a/src/extension/param/radiobutton.cpp +++ b/src/extension/param/radiobutton.cpp @@ -18,10 +18,6 @@ # include "config.h" #endif -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include <gtkmm/box.h> #include <gtkmm/comboboxtext.h> #include <gtkmm/radiobutton.h> diff --git a/src/extension/param/string.cpp b/src/extension/param/string.cpp index d6e438db5..4e525ff73 100644 --- a/src/extension/param/string.cpp +++ b/src/extension/param/string.cpp @@ -10,10 +10,6 @@ # include "config.h" #endif -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include <gtkmm/adjustment.h> #include <gtkmm/box.h> #include <gtkmm/spinbutton.h> diff --git a/src/extension/prefdialog.h b/src/extension/prefdialog.h index 905d35744..4714c2c3f 100644 --- a/src/extension/prefdialog.h +++ b/src/extension/prefdialog.h @@ -10,19 +10,10 @@ #ifndef INKSCAPE_EXTENSION_DIALOG_H__ #define INKSCAPE_EXTENSION_DIALOG_H__ -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif - -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include <gtkmm/dialog.h> #include <glibmm/value.h> #include <glibmm/ustring.h> - namespace Gtk { class CheckButton; } diff --git a/src/file.cpp b/src/file.cpp index f9e259fe2..984bf7e08 100644 --- a/src/file.cpp +++ b/src/file.cpp @@ -769,7 +769,7 @@ file_save_remote(SPDocument */*doc*/, return false; } - result = gnome_vfs_create (&to_handle, uri_local, GNOME_VFS_OPEN_WRITE, FALSE, GNOME_VFS_PERM_USER_ALL); + gnome_vfs_create (&to_handle, uri_local, GNOME_VFS_OPEN_WRITE, FALSE, GNOME_VFS_PERM_USER_ALL); result = gnome_vfs_open (&to_handle, uri_local, GNOME_VFS_OPEN_WRITE); if (result != GNOME_VFS_OK) { @@ -782,8 +782,8 @@ file_save_remote(SPDocument */*doc*/, result = gnome_vfs_read (from_handle, buffer, 8192, &bytes_read); if ((result == GNOME_VFS_ERROR_EOF) &&(!bytes_read)){ - result = gnome_vfs_close (from_handle); - result = gnome_vfs_close (to_handle); + gnome_vfs_close (from_handle); + gnome_vfs_close (to_handle); return true; } @@ -1069,7 +1069,7 @@ void sp_import_document(SPDesktop *desktop, SPDocument *clipdoc, bool in_place) desktop->doc()->importDefs(clipdoc); // copy objects - GSList *pasted_objects = NULL; + std::vector<Inkscape::XML::Node*> pasted_objects; for (Inkscape::XML::Node *obj = root->firstChild() ; obj ; obj = obj->next()) { // Don't copy metadata, defs, named views and internal clipboard contents to the document if (!strcmp(obj->name(), "svg:defs")) { @@ -1088,9 +1088,8 @@ void sp_import_document(SPDesktop *desktop, SPDocument *clipdoc, bool in_place) target_parent->appendChild(obj_copy); Inkscape::GC::release(obj_copy); - pasted_objects = g_slist_prepend(pasted_objects, (gpointer) obj_copy); + pasted_objects.push_back(obj_copy); } - // Change the selection to the freshly pasted objects Inkscape::Selection *selection = desktop->getSelection(); selection->setReprList(pasted_objects); @@ -1129,8 +1128,6 @@ void sp_import_document(SPDesktop *desktop, SPDocument *clipdoc, bool in_place) sp_selection_move_relative(selection, offset); } - - g_slist_free(pasted_objects); } diff --git a/src/gradient-chemistry.cpp b/src/gradient-chemistry.cpp index aecf1aa35..409e9f0e6 100644 --- a/src/gradient-chemistry.cpp +++ b/src/gradient-chemistry.cpp @@ -1068,7 +1068,7 @@ void sp_item_gradient_set_coords(SPItem *item, GrPointType point_type, guint poi // using X-coordinates only to determine the offset, assuming p has been snapped to the vector from begin to end. Geom::Point begin(lg->x1.computed, lg->y1.computed); Geom::Point end(lg->x2.computed, lg->y2.computed); - double offset = Geom::LineSegment(begin, end).nearestPoint(p); + double offset = Geom::LineSegment(begin, end).nearestTime(p); SPGradient *vector = sp_gradient_get_forked_vector_if_necessary (lg, false); lg->ensureVector(); lg->vector.stops.at(point_i).offset = offset; @@ -1163,7 +1163,7 @@ void sp_item_gradient_set_coords(SPItem *item, GrPointType point_type, guint poi { Geom::Point start = Geom::Point (rg->cx.computed, rg->cy.computed); Geom::Point end = Geom::Point (rg->cx.computed + rg->r.computed, rg->cy.computed); - double offset = Geom::LineSegment(start, end).nearestPoint(p); + double offset = Geom::LineSegment(start, end).nearestTime(p); SPGradient *vector = sp_gradient_get_forked_vector_if_necessary (rg, false); rg->ensureVector(); rg->vector.stops.at(point_i).offset = offset; @@ -1180,7 +1180,7 @@ void sp_item_gradient_set_coords(SPItem *item, GrPointType point_type, guint poi { Geom::Point start = Geom::Point (rg->cx.computed, rg->cy.computed); Geom::Point end = Geom::Point (rg->cx.computed, rg->cy.computed - rg->r.computed); - double offset = Geom::LineSegment(start, end).nearestPoint(p); + double offset = Geom::LineSegment(start, end).nearestTime(p); SPGradient *vector = sp_gradient_get_forked_vector_if_necessary(rg, false); rg->ensureVector(); rg->vector.stops.at(point_i).offset = offset; @@ -1570,8 +1570,9 @@ void sp_gradient_invert_selected_gradients(SPDesktop *desktop, Inkscape::PaintTa { Inkscape::Selection *selection = desktop->getSelection(); - for (GSList const* i = selection->itemList(); i != NULL; i = i->next) { - sp_item_gradient_invert_vector_color(SP_ITEM(i->data), fill_or_stroke); + const std::vector<SPItem*> list=selection->itemList(); + for (std::vector<SPItem*>::const_iterator i = list.begin(); i != list.end(); i++) { + sp_item_gradient_invert_vector_color(*i, fill_or_stroke); } // we did an undoable action @@ -1594,9 +1595,10 @@ void sp_gradient_reverse_selected_gradients(SPDesktop *desktop) if (drag && drag->selected) { drag->selected_reverse_vector(); } else { // If no drag or no dragger selected, act on selection (both fill and stroke gradients) - for (GSList const* i = selection->itemList(); i != NULL; i = i->next) { - sp_item_gradient_reverse_vector(SP_ITEM(i->data), Inkscape::FOR_FILL); - sp_item_gradient_reverse_vector(SP_ITEM(i->data), Inkscape::FOR_STROKE); + const std::vector<SPItem*> list=selection->itemList(); + for (std::vector<SPItem*>::const_iterator i = list.begin(); i != list.end(); i++) { + sp_item_gradient_reverse_vector(*i, Inkscape::FOR_FILL); + sp_item_gradient_reverse_vector(*i, Inkscape::FOR_STROKE); } } diff --git a/src/gradient-drag.cpp b/src/gradient-drag.cpp index cb2bd737f..b5a988729 100644 --- a/src/gradient-drag.cpp +++ b/src/gradient-drag.cpp @@ -378,7 +378,7 @@ SPStop *GrDrag::addStopNearPoint(SPItem *item, Geom::Point mouse_p, double toler Geom::Point begin = getGradientCoords(item, POINT_LG_BEGIN, 0, fill_or_stroke); Geom::Point end = getGradientCoords(item, POINT_LG_END, 0, fill_or_stroke); Geom::LineSegment ls(begin, end); - double offset = ls.nearestPoint(mouse_p); + double offset = ls.nearestTime(mouse_p); Geom::Point nearest = ls.pointAt(offset); double dist_screen = Geom::distance(mouse_p, nearest); if ( dist_screen < tolerance ) { @@ -391,7 +391,7 @@ SPStop *GrDrag::addStopNearPoint(SPItem *item, Geom::Point mouse_p, double toler Geom::Point begin = getGradientCoords(item, POINT_RG_CENTER, 0, fill_or_stroke); Geom::Point end = getGradientCoords(item, POINT_RG_R1, 0, fill_or_stroke); Geom::LineSegment ls(begin, end); - double offset = ls.nearestPoint(mouse_p); + double offset = ls.nearestTime(mouse_p); Geom::Point nearest = ls.pointAt(offset); double dist_screen = Geom::distance(mouse_p, nearest); if ( dist_screen < tolerance ) { @@ -403,7 +403,7 @@ SPStop *GrDrag::addStopNearPoint(SPItem *item, Geom::Point mouse_p, double toler } else { end = getGradientCoords(item, POINT_RG_R2, 0, fill_or_stroke); ls = Geom::LineSegment(begin, end); - offset = ls.nearestPoint(mouse_p); + offset = ls.nearestTime(mouse_p); nearest = ls.pointAt(offset); dist_screen = Geom::distance(mouse_p, nearest); if ( dist_screen < tolerance ) { @@ -442,7 +442,7 @@ SPStop *GrDrag::addStopNearPoint(SPItem *item, Geom::Point mouse_p, double toler p[2] = patch.getPoint( 0, 2 ) * transform; p[3] = patch.getPoint( 0, 3 ) * transform; Geom::BezierCurveN<3> b( p[0], p[1], p[2], p[3] ); - Geom::Coord coord = b.nearestPoint( mouse_p ); + Geom::Coord coord = b.nearestTime( mouse_p ); Geom::Point nearest = b( coord ); double dist_screen = Geom::L2 ( mouse_p - nearest ); if ( dist_screen < closest ) { @@ -460,7 +460,7 @@ SPStop *GrDrag::addStopNearPoint(SPItem *item, Geom::Point mouse_p, double toler p[2] = patch.getPoint( 1, 2 ) * transform; p[3] = patch.getPoint( 1, 3 ) * transform; Geom::BezierCurveN<3> b( p[0], p[1], p[2], p[3] ); - Geom::Coord coord = b.nearestPoint( mouse_p ); + Geom::Coord coord = b.nearestTime( mouse_p ); Geom::Point nearest = b( coord ); double dist_screen = Geom::L2 ( mouse_p - nearest ); if ( dist_screen < closest ) { @@ -478,7 +478,7 @@ SPStop *GrDrag::addStopNearPoint(SPItem *item, Geom::Point mouse_p, double toler p[2] = patch.getPoint( 2, 2 ) * transform; p[3] = patch.getPoint( 2, 3 ) * transform; Geom::BezierCurveN<3> b( p[0], p[1], p[2], p[3] ); - Geom::Coord coord = b.nearestPoint( mouse_p ); + Geom::Coord coord = b.nearestTime( mouse_p ); Geom::Point nearest = b( coord ); double dist_screen = Geom::L2 ( mouse_p - nearest ); if ( dist_screen < closest ) { @@ -496,7 +496,7 @@ SPStop *GrDrag::addStopNearPoint(SPItem *item, Geom::Point mouse_p, double toler p[2] = patch.getPoint( 3, 2 ) * transform; p[3] = patch.getPoint( 3, 3 ) * transform; Geom::BezierCurveN<3> b( p[0], p[1], p[2], p[3] ); - Geom::Coord coord = b.nearestPoint( mouse_p ); + Geom::Coord coord = b.nearestTime( mouse_p ); Geom::Point nearest = b( coord ); double dist_screen = Geom::L2 ( mouse_p - nearest ); if ( dist_screen < closest ) { @@ -603,7 +603,7 @@ bool GrDrag::dropColor(SPItem */*item*/, gchar const *c, Geom::Point p) for (GSList *l = lines; (l != NULL) && (!over_line); l = l->next) { SPCtrlLine *line = (SPCtrlLine*) l->data; Geom::LineSegment ls(line->s, line->e); - Geom::Point nearest = ls.pointAt(ls.nearestPoint(p)); + Geom::Point nearest = ls.pointAt(ls.nearestTime(p)); double dist_screen = Geom::L2(p - nearest) * desktop->current_zoom(); if (line->item && dist_screen < 5) { SPStop *stop = addStopNearPoint(line->item, p, 5/desktop->current_zoom()); @@ -1018,10 +1018,10 @@ static void gr_knot_moved_midpoint_handler(SPKnot */*knot*/, Geom::Point const & if (state & GDK_CONTROL_MASK) { Geom::LineSegment ls(low_lim, high_lim); - p = ls.pointAt(round(ls.nearestPoint(p) / snap_fraction) * snap_fraction); + p = ls.pointAt(round(ls.nearestTime(p) / snap_fraction) * snap_fraction); } else { Geom::LineSegment ls(low_lim, high_lim); - p = ls.pointAt(ls.nearestPoint(p)); + p = ls.pointAt(ls.nearestTime(p)); if (!(state & GDK_SHIFT_MASK)) { Inkscape::Snapper::SnapConstraint cl(low_lim, high_lim - low_lim); SPDesktop *desktop = dragger->parent->desktop; @@ -1155,7 +1155,7 @@ static void gr_knot_clicked_handler(SPKnot */*knot*/, guint state, gpointer data break; default: - break; + return; } @@ -2082,9 +2082,9 @@ void GrDrag::updateDraggers() this->draggers = NULL; g_return_if_fail(this->selection != NULL); - - for (GSList const* i = this->selection->itemList(); i != NULL; i = i->next) { - SPItem *item = SP_ITEM(i->data); + std::vector<SPItem*> list = this->selection->itemList(); + for (std::vector<SPItem*>::const_iterator i = list.begin(); i != list.end(); i++) { + SPItem *item = *i; SPStyle *style = item->style; if (style && (style->fill.isPaintserver())) { @@ -2151,9 +2151,9 @@ void GrDrag::updateLines() g_return_if_fail(this->selection != NULL); - for (GSList const* i = this->selection->itemList(); i != NULL; i = i->next) { - - SPItem *item = SP_ITEM(i->data); + std::vector<SPItem*> list = this->selection->itemList(); + for (std::vector<SPItem*>::const_iterator i = list.begin(); i != list.end(); i++) { + SPItem *item = *i; SPStyle *style = item->style; @@ -2295,8 +2295,9 @@ void GrDrag::updateLevels() g_return_if_fail (this->selection != NULL); - for (GSList const* i = this->selection->itemList(); i != NULL; i = i->next) { - SPItem *item = SP_ITEM(i->data); + std::vector<SPItem*> list = this->selection->itemList(); + for (std::vector<SPItem*>::const_iterator i = list.begin(); i != list.end(); i++) { + SPItem *item = *i; Geom::OptRect rect = item->desktopVisualBounds(); if (rect) { // Remember the edges of the bbox and the center axis @@ -2393,7 +2394,7 @@ void GrDrag::selected_move(double x, double y, bool write_repr, bool scale_radia gr_midpoint_limits(dragger, server, &begin, &end, &low_lim, &high_lim, &moving); Geom::LineSegment ls(low_lim, high_lim); - Geom::Point p = ls.pointAt(ls.nearestPoint(dragger->point + Geom::Point(x,y))); + Geom::Point p = ls.pointAt(ls.nearestTime(dragger->point + Geom::Point(x,y))); Geom::Point displacement = p - dragger->point; for (GSList const* i = moving; i != NULL; i = i->next) { diff --git a/src/graphlayout.cpp b/src/graphlayout.cpp index e5d61ab64..40994347c 100644 --- a/src/graphlayout.cpp +++ b/src/graphlayout.cpp @@ -88,9 +88,9 @@ struct CheckProgress : TestConvergence { * Scans the items list and places those items that are * not connectors in filtered */ -void filterConnectors(GSList const *const items, list<SPItem *> &filtered) { - for(GSList *i=(GSList *)items; i!=NULL; i=i->next) { - SPItem *item=SP_ITEM(i->data); +void filterConnectors(std::vector<SPItem*> const &items, list<SPItem *> &filtered) { + for(std::vector<SPItem*>::const_iterator i = items.begin();i !=items.end(); i++){ + SPItem *item = *i; if(!isConnector(item)) { filtered.push_back(item); } @@ -101,8 +101,8 @@ void filterConnectors(GSList const *const items, list<SPItem *> &filtered) { * connectors between them, and uses graph layout techniques to find * a nice layout */ -void graphlayout(GSList const *const items) { - if(!items) { +void graphlayout(std::vector<SPItem*> const &items) { + if(items.empty()) { return; } diff --git a/src/graphlayout.h b/src/graphlayout.h index 0ffb645b6..9794dd6b5 100644 --- a/src/graphlayout.h +++ b/src/graphlayout.h @@ -19,10 +19,10 @@ typedef struct _GSList GSList; class SPItem; -void graphlayout(GSList const *const items); +void graphlayout(std::vector<SPItem*> const &items); bool isConnector(SPItem const *const item); -void filterConnectors(GSList const *const items, std::list<SPItem *> &filtered); +void filterConnectors(std::vector<SPItem*> const &items, std::list<SPItem *> &filtered); #endif // SEEN_GRAPHLAYOUT_H diff --git a/src/helper-fns.h b/src/helper-fns.h index 2f1829c37..79771a001 100644 --- a/src/helper-fns.h +++ b/src/helper-fns.h @@ -78,7 +78,7 @@ inline std::vector<gdouble> helperfns_read_vector(const gchar* value){ g_warning("helper-fns::helperfns_read_vector() Unable to convert \"%s\" to number", beg); // We could leave this out, too. If strtod can't convert // anything, it will return zero. - ret = 0; + // ret = 0; break; } v.push_back(ret); diff --git a/src/helper/geom-curves.h b/src/helper/geom-curves.h index 4586a346c..7357403f7 100644 --- a/src/helper/geom-curves.h +++ b/src/helper/geom-curves.h @@ -14,15 +14,13 @@ * Released under GNU GPL */ -#include <2geom/hvlinesegment.h> #include <2geom/line.h> #include <2geom/bezier-curve.h> /// \todo un-inline this function -inline bool is_straight_curve(Geom::Curve const & c) { - if( dynamic_cast<Geom::LineSegment const*>(&c) || - dynamic_cast<Geom::HLineSegment const*>(&c) || - dynamic_cast<Geom::VLineSegment const*>(&c) ) +inline bool is_straight_curve(Geom::Curve const & c) +{ + if( dynamic_cast<Geom::LineSegment const*>(&c) ) { return true; } @@ -31,7 +29,7 @@ inline bool is_straight_curve(Geom::Curve const & c) { Geom::BezierCurve const *curve = dynamic_cast<Geom::BezierCurve const *>(&c); if (curve) { Geom::Line line(curve->initialPoint(), curve->finalPoint()); - std::vector<Geom::Point> pts = curve->points(); + std::vector<Geom::Point> pts = curve->controlPoints(); for (unsigned i = 1; i < pts.size() - 1; ++i) { if (!are_near(pts[i], line)) return false; diff --git a/src/helper/geom-pathstroke.cpp b/src/helper/geom-pathstroke.cpp index eb0c432c6..c73a9e9e7 100644 --- a/src/helper/geom-pathstroke.cpp +++ b/src/helper/geom-pathstroke.cpp @@ -1,7 +1,8 @@ -/* Author: +/* Authors: * Liam P. White + * Tavmjong Bah * - * Copyright (C) 2014-2015 Author + * Copyright (C) 2014-2015 Authors * * Released under GNU GPL, read the file 'COPYING' for more information */ @@ -10,89 +11,20 @@ #include <2geom/path-sink.h> #include <2geom/point.h> #include <2geom/bezier-curve.h> -#include <2geom/svg-elliptical-arc.h> +#include <2geom/elliptical-arc.h> #include <2geom/sbasis-to-bezier.h> // cubicbezierpath_from_sbasis #include <2geom/path-intersection.h> +#include <2geom/circle.h> #include "helper/geom-pathstroke.h" namespace Geom { -// 2geom/circle-circle.cpp, no header -int circle_circle_intersection(Point X0, double r0, Point X1, double r1, Point &p0, Point &p1); - -/** - * Determine the intersection points between a circle C0 and a line defined - * by two points, X0 and X1. - * - * Which intersection point is assigned to p0 or p1 is unspecified, and callers - * should not depend on any particular intersection always being assigned to p0. - * - * Returns: - * If the line and circle do not cross, 0 is returned. - * If solution(s) exist, 2 is returned, and the results are written to p0 and p1. - */ -static int circle_line_intersection(Circle C0, Point X0, Point X1, Point &p0, Point &p1) -{ - /* equation of a circle: (x - h)^2 + (y - k)^2 = r^2 */ - Coord r = C0.ray(); - Coord h = C0.center()[X]; - Coord k = C0.center()[Y]; - - Coord x0, y0; - Coord x1, y1; - - if (are_near(X1[X], X0[X])) { - /* slope is undefined (vertical line) */ - Coord c = X0[X]; - Coord det = r*r - (c-h)*(c-h); - - /* no intersection */ - if (det < 0) - return 0; - - /* solve for y */ - y0 = k + std::sqrt(det); - y1 = k - std::sqrt(det); - - // x == c (always) - x0 = c; - x1 = c; - } else { - /* equation of a line: y = mx + b */ - Coord m = (X1[Y] - X0[Y]) / (X1[X] - X0[X]); - Coord b = X0[Y] - m*X0[X]; - - /* obtain quadratic for x: */ - Coord A = m*m + 1; - Coord B = 2*h - 2*b*m + 2*k*m; - Coord C = b*b + h*h + k*k - r*r - 2*b*k; - - Coord det = B*B - 4*A*C; - - /* no intersection, circle and line do not cross */ - if (det < 0) - return 0; - - /* solve quadratic */ - x0 = (B + std::sqrt(det)) / (2*A); - x1 = (B - std::sqrt(det)) / (2*A); - - /* substitute the calculated x times to determine the y values */ - y0 = m*x0 + b; - y1 = m*x1 + b; - } - - p0 = Point(x0, y0); - p1 = Point(x1, y1); - - return 2; -} static Point intersection_point(Point origin_a, Point vector_a, Point origin_b, Point vector_b) { - Coord denom = cross(vector_b, vector_a); + Coord denom = cross(vector_a, vector_b); if (!are_near(denom,0.)) { - Coord t = (cross(origin_a,vector_b) + cross(vector_b,origin_b)) / denom; + Coord t = (cross(vector_b, origin_a) + cross(origin_b, vector_b)) / denom; return origin_a + vector_a*t; } return Point(infinity(), infinity()); @@ -127,113 +59,135 @@ static Circle touching_circle( D2<SBasis> const &curve, double t, double tol=0.0 namespace { -// Join functions may: -// - inspect any curve of the current path -// - append any type of curve to the current path -// - inspect the outgoing path -// -// Join functions must: -// - append the outgoing curve -// OR -// - end at outgoing.finalPoint +// Internal data structure + +struct join_data { + join_data(Geom::Path &_res, Geom::Path const&_outgoing, Geom::Point _in_tang, Geom::Point _out_tang, double _miter, double _width) + : res(_res), outgoing(_outgoing), in_tang(_in_tang), out_tang(_out_tang), miter(_miter), width(_width) {}; + + // contains the current path that is being built on + Geom::Path &res; -typedef void join_func(Geom::Path& res, Geom::Curve const& outgoing, double miter, double width); + // contains the next curve to append + Geom::Path const& outgoing; -void bevel_join(Geom::Path& res, Geom::Curve const& outgoing, double /*miter*/, double /*width*/) + // input tangents + Geom::Point in_tang; + Geom::Point out_tang; + + // line parameters + double miter; + double width; +}; + +// Join functions must append the outgoing path + +typedef void join_func(join_data jd); + +void bevel_join(join_data jd) { - res.appendNew<Geom::LineSegment>(outgoing.initialPoint()); - res.append(outgoing); + jd.res.appendNew<Geom::LineSegment>(jd.outgoing.initialPoint()); + jd.res.append(jd.outgoing); } -void round_join(Geom::Path& res, Geom::Curve const& outgoing, double /*miter*/, double width) +void round_join(join_data jd) { - res.appendNew<Geom::SVGEllipticalArc>(width, width, 0, false, width <= 0, outgoing.initialPoint()); - res.append(outgoing); + jd.res.appendNew<Geom::EllipticalArc>(jd.width, jd.width, 0, false, jd.width <= 0, jd.outgoing.initialPoint()); + jd.res.append(jd.outgoing); } -void miter_join_internal(Geom::Path& res, Geom::Curve const& outgoing, double miter, double width, bool clip) +void miter_join_internal(join_data jd, bool clip) { - Geom::Curve const& incoming = res.back(); - Geom::Point tang1 = Geom::unitTangentAt(reverse(incoming.toSBasis()), 0.); - Geom::Point tang2 = outgoing.unitTangentAt(0); - Geom::Point p = Geom::intersection_point(incoming.finalPoint(), tang1, outgoing.initialPoint(), tang2); + using namespace Geom; + + Curve const& incoming = jd.res.back(); + Curve const& outgoing = jd.outgoing.front(); + Path &res = jd.res; + double width = jd.width, miter = jd.miter; + + Point tang1 = jd.in_tang; + Point tang2 = jd.out_tang; + Point p = intersection_point(incoming.finalPoint(), tang1, outgoing.initialPoint(), tang2); bool satisfied = false; bool inc_ls = res.back_open().degreesOfFreedom() <= 4; if (p.isFinite()) { // check size of miter - Geom::Point point_on_path = incoming.finalPoint() + Geom::rot90(tang1)*width; - satisfied = Geom::distance(p, point_on_path) <= miter * 2.0 * width; + Point point_on_path = incoming.finalPoint() + rot90(tang1)*width; + satisfied = distance(p, point_on_path) <= miter * 2.0 * width; if (satisfied) { // miter OK, check to see if we can do a relocation if (inc_ls) { res.setFinal(p); } else { - res.appendNew<Geom::LineSegment>(p); + res.appendNew<LineSegment>(p); } } else if (clip) { // miter needs clipping, find two points - Geom::Point bisector_versor = Geom::Line(point_on_path, p).versor(); - Geom::Point point_limit = point_on_path + miter * 2.0 * width * bisector_versor; + Point bisector_versor = Line(point_on_path, p).versor(); + Point point_limit = point_on_path + miter * 2.0 * width * bisector_versor; - Geom::Point p1 = Geom::intersection_point(incoming.finalPoint(), tang1, point_limit, bisector_versor.cw()); - Geom::Point p2 = Geom::intersection_point(outgoing.initialPoint(), tang2, point_limit, bisector_versor.cw()); + Point p1 = intersection_point(incoming.finalPoint(), tang1, point_limit, bisector_versor.cw()); + Point p2 = intersection_point(outgoing.initialPoint(), tang2, point_limit, bisector_versor.cw()); if (inc_ls) { res.setFinal(p1); } else { - res.appendNew<Geom::LineSegment>(p1); + res.appendNew<LineSegment>(p1); } - res.appendNew<Geom::LineSegment>(p2); + res.appendNew<LineSegment>(p2); } } - res.appendNew<Geom::LineSegment>(outgoing.initialPoint()); + res.appendNew<LineSegment>(outgoing.initialPoint()); // check if we can do another relocation bool out_ls = outgoing.degreesOfFreedom() <= 4; - if ( (satisfied || clip) && out_ls) { + if ((satisfied || clip) && out_ls) { res.setFinal(outgoing.finalPoint()); } else { res.append(outgoing); } -} -void miter_join(Geom::Path& res, Geom::Curve const& outgoing, double miter, double width) { - miter_join_internal( res, outgoing, miter, width, false ); + // either way, add the rest of the path + res.insert(res.end(), ++jd.outgoing.begin(), jd.outgoing.end()); } -void miter_clip_join(Geom::Path& res, Geom::Curve const& outgoing, double miter, double width) { - miter_join_internal( res, outgoing, miter, width, true ); -} +void miter_join(join_data jd) { miter_join_internal(jd, false); } +void miter_clip_join(join_data jd) { miter_join_internal(jd, true); } -Geom::Point pick_solution(Geom::Point points[2], Geom::Point tang2, Geom::Point endPt) +Geom::Point pick_solution(std::vector<Geom::ShapeIntersection> points, Geom::Point tang2, Geom::Point endPt) { + assert(points.size() == 2); Geom::Point sol; - if ( dot(tang2,points[0]-endPt) > 0 ) { + if ( dot(tang2, points[0].point() - endPt) > 0 ) { // points[0] is bad, choose points[1] sol = points[1]; - } else if ( dot(tang2,points[1]-endPt) > 0 ) { // points[0] could be good, now check points[1] + } else if ( dot(tang2, points[1].point() - endPt) > 0 ) { // points[0] could be good, now check points[1] // points[1] is bad, choose points[0] sol = points[0]; } else { // both points are good, choose nearest - sol = ( distanceSq(endPt, points[0]) < distanceSq(endPt, points[1]) ) ? points[0] : points[1]; + sol = ( distanceSq(endPt, points[0].point()) < distanceSq(endPt, points[1].point()) ) + ? points[0].point() : points[1].point(); } return sol; } -void extrapolate_join(Geom::Path& res, Geom::Curve const& outgoing, double miter, double width) +void extrapolate_join(join_data jd) { using namespace Geom; + Geom::Path &res = jd.res; Geom::Curve const& incoming = res.back(); + Geom::Curve const& outgoing = jd.outgoing.front(); Geom::Point startPt = incoming.finalPoint(); Geom::Point endPt = outgoing.initialPoint(); - Geom::Point tang1 = Geom::unitTangentAt(reverse(incoming.toSBasis()), 0.); - Geom::Point tang2 = outgoing.unitTangentAt(0); + Geom::Point tang1 = jd.in_tang; + Geom::Point tang2 = jd.out_tang; + double width = jd.width, miter = jd.miter; Geom::Circle circle1 = Geom::touching_circle(Geom::reverse(incoming.toSBasis()), 0.); Geom::Circle circle2 = Geom::touching_circle(outgoing.toSBasis(), 0); @@ -241,9 +195,8 @@ void extrapolate_join(Geom::Path& res, Geom::Curve const& outgoing, double miter bool inc_ls = !circle1.center().isFinite(); bool out_ls = !circle2.center().isFinite(); - Geom::Point points[2]; + std::vector<Geom::ShapeIntersection> points; - int solutions = 0; Geom::EllipticalArc *arc1 = NULL; Geom::EllipticalArc *arc2 = NULL; Geom::Point sol; @@ -252,35 +205,31 @@ void extrapolate_join(Geom::Path& res, Geom::Curve const& outgoing, double miter if (!inc_ls && !out_ls) { // Two circles - solutions = Geom::circle_circle_intersection(circle1.center(), circle1.ray(), - circle2.center(), circle2.ray(), - points[0], points[1]); - if (solutions == 2) { + points = circle1.intersect(circle2); + if (points.size() == 2) { sol = pick_solution(points, tang2, endPt); - arc1 = circle1.arc(startPt, 0.5*(startPt+sol), sol, true); - arc2 = circle2.arc(sol, 0.5*(sol+endPt), endPt, true); + arc1 = circle1.arc(startPt, 0.5*(startPt+sol), sol); + arc2 = circle2.arc(sol, 0.5*(sol+endPt), endPt); } } else if (inc_ls && !out_ls) { // Line and circle - solutions = Geom::circle_line_intersection(circle2, incoming.initialPoint(), incoming.finalPoint(), points[0], points[1]); - - if (solutions == 2) { + points = circle2.intersect(Line(incoming.initialPoint(), incoming.finalPoint())); + if (points.size() == 2) { sol = pick_solution(points, tang2, endPt); - arc2 = circle2.arc(sol, 0.5*(sol+endPt), endPt, true); + arc2 = circle2.arc(sol, 0.5*(sol+endPt), endPt); } } else if (!inc_ls && out_ls) { // Circle and line - solutions = Geom::circle_line_intersection(circle1, outgoing.initialPoint(), outgoing.finalPoint(), points[0], points[1]); - - if (solutions == 2) { + points = circle1.intersect(Line(outgoing.initialPoint(), outgoing.finalPoint())); + if (points.size() == 2) { sol = pick_solution(points, tang2, endPt); - arc1 = circle1.arc(startPt, 0.5*(sol+startPt), sol, true); + arc1 = circle1.arc(startPt, 0.5*(sol+startPt), sol); } } - if (solutions != 2) + if (points.size() != 2) // no solutions available, fall back to miter - return miter_clip_join(res, outgoing, miter, width); + return miter_clip_join(jd); // We have a solution, thus sol is defined. p1 = sol; @@ -322,25 +271,24 @@ void extrapolate_join(Geom::Path& res, Geom::Curve const& outgoing, double miter Geom::Ray end_ray(center, sol); Geom::Line limit_line(center, 0); // Angle set below - if (Geom::cross(start_ray.versor(), end_ray.versor()) > 0) { + if (Geom::cross(start_ray.versor(), end_ray.versor()) < 0) { limit_line.setAngle(start_ray.angle() - limit_angle); } else { limit_line.setAngle(start_ray.angle() + limit_angle); } - Geom::EllipticalArc *arc_center = circle_center.arc(point_on_path, 0.5*(point_on_path + sol), sol, true); + Geom::EllipticalArc *arc_center = circle_center.arc(point_on_path, 0.5*(point_on_path + sol), sol); if (arc_center && arc_center->sweepAngle() > limit_angle) { // We need to clip clipped = true; if (!inc_ls) { // Incoming circular - solutions = Geom::circle_line_intersection(circle1, limit_line.pointAt(0), limit_line.pointAt(1), points[0], points[1]); - - if (solutions == 2) { + points = circle1.intersect(limit_line); + if (points.size() == 2) { p1 = pick_solution(points, tang2, endPt); delete arc1; - arc1 = circle1.arc(startPt, 0.5*(p1+startPt), p1, true); + arc1 = circle1.arc(startPt, 0.5*(p1+startPt), p1); } } else { p1 = Geom::intersection_point(startPt, tang1, limit_line.pointAt(0), limit_line.versor()); @@ -348,12 +296,11 @@ void extrapolate_join(Geom::Path& res, Geom::Curve const& outgoing, double miter if (!out_ls) { // Outgoing circular - solutions = Geom::circle_line_intersection(circle2, limit_line.pointAt(0), limit_line.pointAt(1), points[0], points[1]); - - if (solutions == 2) { + points = circle2.intersect(limit_line); + if (points.size() == 2) { p2 = pick_solution(points, tang1, endPt); delete arc2; - arc2 = circle2.arc(p2, 0.5*(p2+endPt), endPt, true); + arc2 = circle2.arc(p2, 0.5*(p2+endPt), endPt); } } else { p2 = Geom::intersection_point(endPt, tang2, limit_line.pointAt(0), limit_line.versor()); @@ -381,75 +328,59 @@ void extrapolate_join(Geom::Path& res, Geom::Curve const& outgoing, double miter // Straight line segment: res.appendNew<Geom::LineSegment>(outgoing.finalPoint()); } - + + // add the rest of the path + res.insert(res.end(), ++jd.outgoing.begin(), jd.outgoing.end()); + delete arc1; delete arc2; } -void join_inside(Geom::Path& res, Geom::Curve const& outgoing) +void join_inside(join_data jd) { - Geom::Curve const& incoming = res.back_open(); - Geom::Crossings cross = Geom::crossings(incoming, outgoing); - - if (!cross.empty()) { - // yeah if we could avoid allocing that'd be great - Geom::Curve *d1 = incoming.portion(0., cross[0].ta); - res.erase_last(); - res.append(*d1); - delete d1; + Geom::Path &res = jd.res; + Geom::Path const& temp = jd.outgoing; + Geom::Crossings cross = Geom::crossings(res, temp); + + int solution = -1; // lol, really hope there aren't more than INT_MAX crossings + if (cross.size() == 1) solution = 0; + else if (cross.size() > 1) { + // I am not sure how well this will work -- we pick the join node closest + // to the cross point of the paths + /*Geom::Point original = res.finalPoint()+Geom::rot90(jd.in_tang)*jd.width; + Geom::Coord trial = Geom::L2(res.pointAt(cross[0].ta)-original); + solution = 0; + for (size_t i = 1; i < cross.size(); ++i) { + //printf("Trying %d\n", i); + Geom::Coord test = Geom::L2(res.pointAt(cross[i].ta)-original); + if (test < trial) { + trial = test; + solution = i; + //printf("Found improved solution: %f\n", trial); + } + }*/ + } - Geom::Curve *d2 = outgoing.portion(cross[0].tb, 1.); - res.setFinal(d2->initialPoint()); - res.append(*d2); - delete d2; + if (solution != -1) { + Geom::Path d1 = res.portion(0., cross[solution].ta); + Geom::Path d2 = temp.portion(cross[solution].tb, temp.size()); + + // Watch for bugs in 2geom crossing regarding severe inflection points + res.clear(); + res.append(d1); + res.setFinal(d2.initialPoint()); + res.append(d2); } else { - res.appendNew<Geom::LineSegment>(outgoing.initialPoint()); - res.append(outgoing); + res.appendNew<Geom::LineSegment>(temp.initialPoint()); + res.append(temp); } } -bool decide(Geom::Curve const& incoming, Geom::Curve const& outgoing) +void tangents(Geom::Point tang[2], Geom::Curve const& incoming, Geom::Curve const& outgoing) { Geom::Point tang1 = Geom::unitTangentAt(reverse(incoming.toSBasis()), 0.); Geom::Point tang2 = outgoing.unitTangentAt(0.); - return (Geom::cross(tang1, tang2) < 0); -} - -void outline_helper(Geom::Path& res, Geom::Path const& to_add, double width, bool on_outside, double miter, Inkscape::LineJoinType join) -{ - if (res.size() == 0 || to_add.size() == 0) - return; - - Geom::Curve const& outgoing = to_add[0]; - if (Geom::are_near(res.finalPoint(), outgoing.initialPoint())) { - // if the points are /that/ close, just ignore this one - res.setFinal(outgoing.initialPoint()); - res.append(outgoing); - return; - } - - if (on_outside) { - join_func *jf; - switch (join) { - case Inkscape::JOIN_BEVEL: - jf = &bevel_join; - break; - case Inkscape::JOIN_ROUND: - jf = &round_join; - break; - case Inkscape::JOIN_EXTRAPOLATE: - jf = &extrapolate_join; - break; - case Inkscape::JOIN_MITER_CLIP: - jf = &miter_clip_join; - break; - default: - jf = &miter_join; - } - jf(res, outgoing, miter, width); - } else { - join_inside(res, outgoing); - } + tang[0] = tang1, tang[1] = tang2; } // Offsetting a line segment is mathematically stable and quick to do @@ -486,12 +417,12 @@ void get_cubic_data(Geom::CubicBezier const& bez, double time, double& len, doub if (Geom::are_near(l, 0)) { return; // this isn't a segment... } - rad = 1e8; + rad = 1e8; } else { - rad = -l * (Geom::dot(der2, der2) / Geom::cross(der3, der2)); + rad = -l * (Geom::dot(der2, der2) / Geom::cross(der2, der3)); } } else { - rad = -l * (Geom::dot(der1, der1) / Geom::cross(der2, der1)); + rad = -l * (Geom::dot(der1, der1) / Geom::cross(der1, der2)); } len = l; } @@ -538,7 +469,7 @@ void offset_cubic(Geom::Path& p, Geom::CubicBezier const& bez, double width, dou // reached maximum recursive depth // don't bother with any more correction if (levels == 0) { - p.append(c, Geom::Path::STITCH_DISCONTINUOUS); + p.append(c); return; } @@ -570,7 +501,7 @@ void offset_quadratic(Geom::Path& p, Geom::QuadraticBezier const& bez, double wi // cheat // it's faster // seriously - std::vector<Geom::Point> points = bez.points(); + std::vector<Geom::Point> points = bez.controlPoints(); Geom::Point b1 = points[0] + (2./3) * (points[1] - points[0]); Geom::Point b2 = b1 + (1./3) * (points[2] - points[0]); Geom::CubicBezier cub = Geom::CubicBezier(points[0], b1, b2, points[2]); @@ -579,7 +510,7 @@ void offset_quadratic(Geom::Path& p, Geom::QuadraticBezier const& bez, double wi void offset_curve(Geom::Path& res, Geom::Curve const* current, double width) { - double const tolerance = 0.005; + double const tolerance = 0.0025; size_t levels = 8; if (current->isDegenerate()) return; // don't do anything @@ -658,7 +589,7 @@ Geom::PathVector outline(Geom::Path const& input, double width, double miter, Li Geom::PathBuilder res; Geom::Path with_dir = half_outline(input, width/2., miter, join); - Geom::Path against_dir = half_outline(input.reverse(), width/2., miter, join); + Geom::Path against_dir = half_outline(input.reversed(), width/2., miter, join); res.moveTo(with_dir[0].initialPoint()); res.append(with_dir); @@ -705,6 +636,10 @@ Geom::Path half_outline(Geom::Path const& input, double width, double miter, Lin Geom::Point tang1 = input[0].unitTangentAt(0); Geom::Point start = input.initialPoint() + tang1 * width; Geom::Path temp; + Geom::Point tang[2]; + + res.setStitching(true); + temp.setStitching(true); res.start(start); @@ -712,7 +647,7 @@ Geom::Path half_outline(Geom::Path const& input, double width, double miter, Lin const size_t k = (input.back_closed().isDegenerate() && input.closed()) ?input.size_default()-1:input.size_default(); for (size_t u = 0; u < k; u += 2) { - temp = Geom::Path(); + temp.clear(); offset_curve(temp, &input[u], width); @@ -720,32 +655,28 @@ Geom::Path half_outline(Geom::Path const& input, double width, double miter, Lin if (u == 0) { res.append(temp); } else { - bool on_outside = decide(input[u-1], input[u]); - outline_helper(res, temp, width, on_outside, miter, join); - if (temp.size() > 0) - res.insert(res.end(), ++temp.begin(), temp.end()); + tangents(tang, input[u-1], input[u]); + outline_join(res, temp, tang[0], tang[1], width, miter, join); } // odd number of paths if (u < k - 1) { - temp = Geom::Path(); + temp.clear(); offset_curve(temp, &input[u+1], width); - bool on_outside = decide(input[u], input[u+1]); - outline_helper(res, temp, width, on_outside, miter, join); - if (temp.size() > 0) - res.insert(res.end(), ++temp.begin(), temp.end()); + tangents(tang, input[u], input[u+1]); + outline_join(res, temp, tang[0], tang[1], width, miter, join); } } if (input.closed()) { Geom::Curve const &c1 = res.back(); Geom::Curve const &c2 = res.front(); - temp = Geom::Path(); + temp.clear(); temp.append(c1); Geom::Path temp2; temp2.append(c2); - bool on_outside = decide(input.back(), input.front()); - outline_helper(temp, temp2, width, on_outside, miter, join); + tangents(tang, input.back(), input.front()); + outline_join(temp, temp2, tang[0], tang[1], width, miter, join); res.erase(res.begin()); res.erase_last(); // @@ -756,6 +687,47 @@ Geom::Path half_outline(Geom::Path const& input, double width, double miter, Lin return res; } +void outline_join(Geom::Path &res, Geom::Path const& temp, Geom::Point in_tang, Geom::Point out_tang, double width, double miter, Inkscape::LineJoinType join) +{ + if (res.size() == 0 || temp.size() == 0) + return; + + Geom::Curve const& outgoing = temp.front(); + if (Geom::are_near(res.finalPoint(), outgoing.initialPoint())) { + // if the points are /that/ close, just ignore this one + res.setFinal(temp.initialPoint()); + res.append(temp); + return; + } + + join_data jd(res, temp, in_tang, out_tang, miter, width); + + bool on_outside = (Geom::cross(in_tang, out_tang) > 0); + + if (on_outside) { + join_func *jf; + switch (join) { + case Inkscape::JOIN_BEVEL: + jf = &bevel_join; + break; + case Inkscape::JOIN_ROUND: + jf = &round_join; + break; + case Inkscape::JOIN_EXTRAPOLATE: + jf = &extrapolate_join; + break; + case Inkscape::JOIN_MITER_CLIP: + jf = &miter_clip_join; + break; + default: + jf = &miter_join; + } + jf(jd); + } else { + join_inside(jd); + } +} + } // namespace Inkscape /* diff --git a/src/helper/geom-pathstroke.h b/src/helper/geom-pathstroke.h index 0cfb9f817..6697273cf 100644 --- a/src/helper/geom-pathstroke.h +++ b/src/helper/geom-pathstroke.h @@ -1,10 +1,11 @@ #ifndef INKSCAPE_HELPER_PATH_STROKE_H #define INKSCAPE_HELPER_PATH_STROKE_H -/* Author: +/* Authors: * Liam P. White + * Tavmjong Bah * - * Copyright (C) 2014-2015 Author + * Copyright (C) 2014-2015 Authors * * Released under GNU GPL, read the file 'COPYING' for more information */ @@ -26,21 +27,54 @@ enum LineCapType { BUTT_FLAT, BUTT_ROUND, BUTT_SQUARE, - BUTT_PEAK, // ? + BUTT_PEAK, // This is not a line ending supported by the SVG standard. }; /** + * Strokes the path given by @a input. + * Joins may behave oddly if the width is negative. + * + * @param[in] input Input path. + * @param[in] width Stroke width. + * @param[in] miter Miter limit. Only used when @a join is one of JOIN_MITER, JOIN_MITER_CLIP, and JOIN_EXTRAPOLATE. + * @param[in] join Line join type used during offset. Member of LineJoinType enum. + * @param[in] cap Line cap type used during stroking. Member of LineCapType enum. + * + * @return Stroked path. + * If the input path is closed, the resultant vector will contain two paths. + * Otherwise, there should be only one in the output. + */ +Geom::PathVector outline(Geom::Path const& input, double width, double miter, LineJoinType join = JOIN_BEVEL, LineCapType cap = BUTT_FLAT); + +/** * Offset the input path by @a width. * Joins may behave oddly if the width is negative. * - * @param input - * @param width Amount to offset. - * @param miter Miter limit. Only used with JOIN_MITER, JOIN_MITER_CLIP, and JOIN_EXTRAPOLATE. - * @param join + * @param[in] input Input path. + * @param[in] width Amount to offset. + * @param[in] miter Miter limit. Only used when @a join is one of JOIN_MITER, JOIN_MITER_CLIP, and JOIN_EXTRAPOLATE. + * @param[in] join Line join type used during offset. Member of LineJoinType enum. + * + * @return Offsetted output. */ Geom::Path half_outline(Geom::Path const& input, double width, double miter, LineJoinType join = JOIN_BEVEL); -Geom::PathVector outline(Geom::Path const& input, double width, double miter, LineJoinType join = JOIN_BEVEL, LineCapType cap = BUTT_FLAT); +/** + * Builds a join on the provided path. + * Joins may behave oddly if the width is negative. + * + * @param[inout] res The path to build the join on. + * The outgoing path (or a portion thereof) will be appended after the join is created. + * Previous segments may be modified as an optimization, beware! + * + * @param[in] outgoing The segment to append on the outgoing portion of the join. + * @param[in] in_tang The end tangent to consider on the input path. + * @param[in] out_tang The begin tangent to consider on the output path. + * @param[in] width + * @param[in] miter + * @param[in] join + */ +void outline_join(Geom::Path &res, Geom::Path const& outgoing, Geom::Point in_tang, Geom::Point out_tang, double width, double miter, LineJoinType join); } // namespace Inkscape diff --git a/src/helper/geom.cpp b/src/helper/geom.cpp index 91689375f..ecb330b01 100644 --- a/src/helper/geom.cpp +++ b/src/helper/geom.cpp @@ -15,8 +15,7 @@ #include <typeinfo> #include <2geom/pathvector.h> #include <2geom/path.h> -#include <2geom/bezier-curve.h> -#include <2geom/hvlinesegment.h> +#include <2geom/curves.h> #include <2geom/transforms.h> #include <2geom/rect.h> #include <2geom/coord.h> @@ -266,14 +265,13 @@ geom_cubic_bbox_wind_distance (Geom::Coord x000, Geom::Coord y000, Geom::Coord tolerance) { Geom::Coord x0, y0, x1, y1, len2; - int needdist, needwind, needline; + int needdist, needwind; const Geom::Coord Px = pt[X]; const Geom::Coord Py = pt[Y]; needdist = 0; needwind = 0; - needline = 0; if (bbox) cubic_bbox (x000, y000, x001, y001, x011, y011, x111, y111, *bbox); @@ -303,8 +301,6 @@ geom_cubic_bbox_wind_distance (Geom::Coord x000, Geom::Coord y000, /* fixme: (Lauris) */ if (((y1 - y0) > 5.0) || ((x1 - x0) > 5.0)) { needdist = 1; - } else { - needline = 1; } } } @@ -315,8 +311,6 @@ geom_cubic_bbox_wind_distance (Geom::Coord x000, Geom::Coord y000, /* fixme: (Lauris) */ if (((y1 - y0) > 5.0) || ((x1 - x0) > 5.0)) { needwind = 1; - } else { - needline = 1; } } } @@ -345,7 +339,7 @@ geom_cubic_bbox_wind_distance (Geom::Coord x000, Geom::Coord y000, geom_cubic_bbox_wind_distance (x000, y000, x00t, y00t, x0tt, y0tt, xttt, yttt, pt, NULL, wind, best, tolerance); geom_cubic_bbox_wind_distance (xttt, yttt, x1tt, y1tt, x11t, y11t, x111, y111, pt, NULL, wind, best, tolerance); - } else if (1 || needline) { + } else { geom_line_wind_distance (x000, y000, x111, y111, pt, wind, best); } } @@ -473,8 +467,8 @@ pathv_to_linear_and_cubic_beziers( Geom::PathVector const &pathv ) for (Geom::PathVector::const_iterator pit = pathv.begin(); pit != pathv.end(); ++pit) { output.push_back( Geom::Path() ); + output.back().setStitching(true); output.back().start( pit->initialPoint() ); - output.back().close( pit->closed() ); for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_open(); ++cit) { if (is_straight_curve(*cit)) { @@ -488,10 +482,13 @@ pathv_to_linear_and_cubic_beziers( Geom::PathVector const &pathv ) } else { // convert all other curve types to cubicbeziers Geom::Path cubicbezier_path = Geom::cubicbezierpath_from_sbasis(cit->toSBasis(), 0.1); + cubicbezier_path.close(false); output.back().append(cubicbezier_path); } } } + + output.back().close( pit->closed() ); } return output; @@ -525,8 +522,7 @@ pathv_to_linear( Geom::PathVector const &pathv, double /*maxdisp*/) } else { /* all others must be Bezier curves */ Geom::BezierCurve const *curve = dynamic_cast<Geom::BezierCurve const *>(&*cit); - Geom::CubicBezier b((*curve)[0], (*curve)[1], (*curve)[2], (*curve)[3]); - std::vector<Geom::Point> bzrpoints = b.points(); + std::vector<Geom::Point> bzrpoints = curve->controlPoints(); Geom::Point A = bzrpoints[0]; Geom::Point B = bzrpoints[1]; Geom::Point C = bzrpoints[2]; @@ -584,7 +580,7 @@ pathv_to_cubicbezier( Geom::PathVector const &pathv) pitCubic.appendNew<Geom::LineSegment>( pitCubic.initialPoint() ); pitCubic.close(true); } - for (Geom::Path::const_iterator cit = pitCubic.begin(); cit != pitCubic.end_open(); ++cit) { + for (Geom::Path::iterator cit = pitCubic.begin(); cit != pitCubic.end_open(); ++cit) { if (is_straight_curve(*cit)) { Geom::CubicBezier b(cit->initialPoint(), cit->pointAt(0.3334) + Geom::Point(cubicGap,cubicGap), cit->finalPoint(), cit->finalPoint()); output.back().append(b); @@ -854,41 +850,6 @@ recursive_bezier4(const double x1, const double y1, recursive_bezier4(x1234, y1234, x234, y234, x34, y34, x4, y4, m_points, level + 1); } - -/** - * rounds all corners of the rectangle 'outwards', i.e. x0 and y0 are floored, x1 and y1 are ceiled. - */ -void round_rectangle_outwards(Geom::Rect & rect) { - Geom::Interval ints[2]; - for (int i=0; i < 2; i++) { - ints[i] = Geom::Interval(std::floor(rect[i][0]), std::ceil(rect[i][1])); - } - rect = Geom::Rect(ints[0], ints[1]); -} - - -namespace Geom { - -bool transform_equalp(Geom::Affine const &m0, Geom::Affine const &m1, Geom::Coord const epsilon) { - return - Geom::are_near(m0[0], m1[0], epsilon) && - Geom::are_near(m0[1], m1[1], epsilon) && - Geom::are_near(m0[2], m1[2], epsilon) && - Geom::are_near(m0[3], m1[3], epsilon); -} - - -bool translate_equalp(Geom::Affine const &m0, Geom::Affine const &m1, Geom::Coord const epsilon) { - return Geom::are_near(m0[4], m1[4], epsilon) && Geom::are_near(m0[5], m1[5], epsilon); -} - - -bool matrix_equalp(Geom::Affine const &m0, Geom::Affine const &m1, Geom::Coord const epsilon) { - return transform_equalp(m0, m1, epsilon) && translate_equalp(m0, m1, epsilon); -} - -} //end namespace Geom - /* Local Variables: mode:c++ diff --git a/src/helper/geom.h b/src/helper/geom.h index 3232d9fd5..d49e2070c 100644 --- a/src/helper/geom.h +++ b/src/helper/geom.h @@ -33,13 +33,6 @@ void recursive_bezier4(const double x1, const double y1, const double x2, const std::vector<Geom::Point> &pointlist, int level); -void round_rectangle_outwards(Geom::Rect & rect); - -namespace Geom{ -bool transform_equalp(Geom::Affine const &m0, Geom::Affine const &m1, Geom::Coord const epsilon); -bool translate_equalp(Geom::Affine const &m0, Geom::Affine const &m1, Geom::Coord const epsilon); -bool matrix_equalp(Geom::Affine const &m0, Geom::Affine const &m1, Geom::Coord const epsilon); -} #endif // INKSCAPE_HELPER_GEOM_H /* diff --git a/src/helper/png-write.cpp b/src/helper/png-write.cpp index 32e50b537..fc365c435 100644 --- a/src/helper/png-write.cpp +++ b/src/helper/png-write.cpp @@ -361,19 +361,19 @@ sp_export_get_rows(guchar const **rows, void **to_free, int row, int num_rows, v /** * Hide all items that are not listed in list, recursively, skipping groups and defs. */ -static void hide_other_items_recursively(SPObject *o, GSList *list, unsigned dkey) +static void hide_other_items_recursively(SPObject *o, const std::vector<SPItem*> &list, unsigned dkey) { if ( SP_IS_ITEM(o) && !SP_IS_DEFS(o) && !SP_IS_ROOT(o) && !SP_IS_GROUP(o) - && !g_slist_find(list, o) ) + && list.end()==find(list.begin(),list.end(),o)) { SP_ITEM(o)->invoke_hide(dkey); } // recurse - if (!g_slist_find(list, o)) { + if (list.end()==find(list.begin(),list.end(),o)) { for ( SPObject *child = o->firstChild() ; child; child = child->getNext() ) { hide_other_items_recursively(child, list, dkey); } @@ -387,7 +387,7 @@ ExportResult sp_export_png_file(SPDocument *doc, gchar const *filename, unsigned long bgcolor, unsigned int (*status) (float, void *), void *data, bool force_overwrite, - GSList *items_only) + const std::vector<SPItem*> &items_only) { return sp_export_png_file(doc, filename, Geom::Rect(Geom::Point(x0,y0),Geom::Point(x1,y1)), width, height, xdpi, ydpi, bgcolor, status, data, force_overwrite, items_only); @@ -399,7 +399,7 @@ ExportResult sp_export_png_file(SPDocument *doc, gchar const *filename, unsigned long bgcolor, unsigned (*status)(float, void *), void *data, bool force_overwrite, - GSList *items_only) + const std::vector<SPItem*> &items_only) { g_return_val_if_fail(doc != NULL, EXPORT_ERROR); g_return_val_if_fail(filename != NULL, EXPORT_ERROR); @@ -457,7 +457,7 @@ ExportResult sp_export_png_file(SPDocument *doc, gchar const *filename, // We show all and then hide all items we don't want, instead of showing only requested items, // because that would not work if the shown item references something in defs - if (items_only) { + if (!items_only.empty()) { hide_other_items_recursively(doc->getRoot(), items_only, dkey); } diff --git a/src/helper/png-write.h b/src/helper/png-write.h index 8c04b25dc..2657fb635 100644 --- a/src/helper/png-write.h +++ b/src/helper/png-write.h @@ -14,8 +14,9 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ -#include <glib.h> #include <2geom/forward.h> + + class SPDocument; enum ExportResult { @@ -33,12 +34,12 @@ ExportResult sp_export_png_file(SPDocument *doc, gchar const *filename, double x0, double y0, double x1, double y1, unsigned long int width, unsigned long int height, double xdpi, double ydpi, unsigned long bgcolor, - unsigned int (*status) (float, void *), void *data, bool force_overwrite = false, GSList *items_only = NULL); + unsigned int (*status) (float, void *), void *data, bool force_overwrite = false, const std::vector<SPItem*> &items_only = std::vector<SPItem*>()); ExportResult sp_export_png_file(SPDocument *doc, gchar const *filename, Geom::Rect const &area, unsigned long int width, unsigned long int height, double xdpi, double ydpi, unsigned long bgcolor, - unsigned int (*status) (float, void *), void *data, bool force_overwrite = false, GSList *items_only = NULL); + unsigned int (*status) (float, void *), void *data, bool force_overwrite = false, const std::vector<SPItem*> &items_only = std::vector<SPItem*>()); #endif // SEEN_SP_PNG_WRITE_H diff --git a/src/inkscape.cpp b/src/inkscape.cpp index 6a0e0f35a..888a64430 100644 --- a/src/inkscape.cpp +++ b/src/inkscape.cpp @@ -22,10 +22,6 @@ #include <map> -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include <gtkmm/messagedialog.h> #include "debug/simple-event.h" #include "debug/event-tracker.h" diff --git a/src/inkview.cpp b/src/inkview.cpp index 2c667237e..8b7492798 100644 --- a/src/inkview.cpp +++ b/src/inkview.cpp @@ -274,7 +274,7 @@ main (int argc, const char **argv) gchar *last_filename = jar_file_reader.get_last_filename(); if (ss.doc) { ss.slides[ss.length++] = strdup (last_filename); - (ss.doc)->setUri (strdup(last_filename)); + (ss.doc)->setUri (last_filename); } g_byte_array_free(gba, TRUE); g_free(last_filename); diff --git a/src/knot-holder-entity.cpp b/src/knot-holder-entity.cpp index b66156b09..173025920 100644 --- a/src/knot-holder-entity.cpp +++ b/src/knot-holder-entity.cpp @@ -140,19 +140,19 @@ KnotHolderEntity::snap_knot_position_constrained(Geom::Point const &p, Inkscape: static gdouble sp_pattern_extract_theta(SPPattern const *pat) { - Geom::Affine transf = pat->patternTransform; + Geom::Affine transf = pat->getTransform(); return Geom::atan2(transf.xAxis()); } static Geom::Point sp_pattern_extract_scale(SPPattern const *pat) { - Geom::Affine transf = pat->patternTransform; + Geom::Affine transf = pat->getTransform(); return Geom::Point( transf.expansionX(), transf.expansionY() ); } static Geom::Point sp_pattern_extract_trans(SPPattern const *pat) { - return Geom::Point(pat->patternTransform[4], pat->patternTransform[5]); + return Geom::Point(pat->getTransform()[4], pat->getTransform()[5]); } void @@ -191,7 +191,7 @@ PatternKnotHolderEntityAngle::knot_get() const { SPPattern *pat = _fill ? SP_PATTERN(item->style->getFillPaintServer()) : SP_PATTERN(item->style->getStrokePaintServer()); - gdouble x = pattern_width(pat); + gdouble x = pat->width(); gdouble y = 0; Geom::Point delta = Geom::Point(x,y); Geom::Point scale = sp_pattern_extract_scale(pat); @@ -240,8 +240,8 @@ PatternKnotHolderEntityScale::knot_set(Geom::Point const &p, Geom::Point const & // Get the new scale from the position of the knotholder Geom::Point d = p_snapped - sp_pattern_extract_trans(pat); - gdouble pat_x = pattern_width(pat); - gdouble pat_y = pattern_height(pat); + gdouble pat_x = pat->width(); + gdouble pat_y = pat->height(); Geom::Scale scl(1); if ( state & GDK_CONTROL_MASK ) { // if ctrl is pressed: use 1:1 scaling @@ -267,10 +267,10 @@ PatternKnotHolderEntityScale::knot_get() const { SPPattern *pat = _fill ? SP_PATTERN(item->style->getFillPaintServer()) : SP_PATTERN(item->style->getStrokePaintServer()); - gdouble x = pattern_width(pat); - gdouble y = pattern_height(pat); + gdouble x = pat->width(); + gdouble y = pat->height(); Geom::Point delta = Geom::Point(x,y); - Geom::Affine a = pat->patternTransform; + Geom::Affine a = pat->getTransform(); a[4] = 0; a[5] = 0; delta = delta * a; diff --git a/src/libavoid/connector.cpp b/src/libavoid/connector.cpp index 40ded7498..36892c668 100644 --- a/src/libavoid/connector.cpp +++ b/src/libavoid/connector.cpp @@ -1476,7 +1476,6 @@ CrossingsInfoPair countRealCrossings(Avoid::Polygon& poly, int prevTurnDir = -1; int startCornerSide = 1; int endCornerSide = 1; - bool reversed = false; if (!front_same) { // If there is a divergence at the beginning, @@ -1485,7 +1484,6 @@ CrossingsInfoPair countRealCrossings(Avoid::Polygon& poly, startCornerSide = Avoid::cornerSide(*c_path[0], *c_path[1], *c_path[2], *p_path[0]) * segDir(*c_path[1], *c_path[2]); - reversed = (startCornerSide != -prevTurnDir); } if (!back_same) { @@ -1497,7 +1495,6 @@ CrossingsInfoPair countRealCrossings(Avoid::Polygon& poly, *c_path[size - 2], *c_path[size - 1], *p_path[size - 1]) * segDir(*c_path[size - 3], *c_path[size - 2]); - reversed = (endCornerSide != -prevTurnDir); } else { @@ -1578,10 +1575,9 @@ CrossingsInfoPair countRealCrossings(Avoid::Polygon& poly, } } #endif - prevTurnDir = 0; if (pointOrders) { - reversed = false; + bool reversed = false; size_t startPt = (front_same) ? 0 : 1; // Orthogonal should always have at least one segment. diff --git a/src/libavoid/graph.cpp b/src/libavoid/graph.cpp index 728f8c085..5b617f123 100644 --- a/src/libavoid/graph.cpp +++ b/src/libavoid/graph.cpp @@ -129,7 +129,9 @@ static inline int orthogTurnOrder(const Point& a, const Point& b, // Note: This method assumes the two Edges that share a common point. bool EdgeInf::rotationLessThan(const VertInf *lastV, const EdgeInf *rhs) const { - if ((_v1 == rhs->_v1) && (_v2 == rhs->_v2)) + assert(_v1 == rhs->_v1 || _v1 == rhs->_v2 || _v2 == rhs->_v1 || _v2 == rhs->_v2 ); + + if ((_v1 == rhs->_v1) && (_v2 == rhs->_v2)) { // Effectively the same visibility edge, so they are equal. return false; diff --git a/src/libavoid/orthogonal.cpp b/src/libavoid/orthogonal.cpp index b5ef8d7e8..466d1dd58 100644 --- a/src/libavoid/orthogonal.cpp +++ b/src/libavoid/orthogonal.cpp @@ -1548,7 +1548,6 @@ extern void generateStaticOrthogonalVisGraph(Router *router) // Process the horizontal sweep thisPos = (totalEvents > 0) ? events[0]->pos : 0; posStartIndex = 0; - posFinishIndex = 0; for (unsigned i = 0; i <= totalEvents; ++i) { // If we have finished the current scanline or all events, then we diff --git a/src/libcroco/cr-fonts.c b/src/libcroco/cr-fonts.c index 3c6896ffd..10f26c99c 100644 --- a/src/libcroco/cr-fonts.c +++ b/src/libcroco/cr-fonts.c @@ -768,7 +768,7 @@ cr_font_weight_get_bolder (enum CRFontWeight a_weight) } else if (a_weight < FONT_WEIGHT_NORMAL) { return FONT_WEIGHT_NORMAL ; } else if (a_weight == FONT_WEIGHT_BOLDER - || a_weight == FONT_WEIGHT_BOLDER) { + || a_weight == FONT_WEIGHT_LIGHTER) { cr_utils_trace_info ("FONT_WEIGHT_BOLDER or FONT_WEIGHT_LIGHTER should not appear here") ; return FONT_WEIGHT_NORMAL ; } else { diff --git a/src/libdepixelize/kopftracer2011.cpp b/src/libdepixelize/kopftracer2011.cpp index 1e769b0c9..96784b14f 100644 --- a/src/libdepixelize/kopftracer2011.cpp +++ b/src/libdepixelize/kopftracer2011.cpp @@ -28,11 +28,6 @@ #include <glibmm.h> -// Build fix under Inkscape build tree -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include <algorithm> #include "kopftracer2011.h" #include "priv/colorspace.h" @@ -667,4 +662,4 @@ inline bool Heuristics::islands(PixelGraph::const_iterator a, fill-column:99 End: */ -// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : diff --git a/src/libdepixelize/priv/splines-kopf2011.h b/src/libdepixelize/priv/splines-kopf2011.h index c586f74b7..c3da58c05 100644 --- a/src/libdepixelize/priv/splines-kopf2011.h +++ b/src/libdepixelize/priv/splines-kopf2011.h @@ -93,7 +93,7 @@ template<class T> void worker(const typename HomogeneousSplines<T>::Polygon &source, Splines::Path &dest, bool optimize) { - dest.pathVector.reserve(source.holes.size() + 1); + //dest.pathVector.reserve(source.holes.size() + 1); for ( int i = 0 ; i != 4 ; ++i ) dest.rgba[i] = source.rgba[i]; diff --git a/src/libnrtype/FontFactory.cpp b/src/libnrtype/FontFactory.cpp index c8f5e1fef..65eb62dda 100644 --- a/src/libnrtype/FontFactory.cpp +++ b/src/libnrtype/FontFactory.cpp @@ -18,9 +18,11 @@ #include <glibmm/i18n.h> #include <pango/pangoft2.h> +#include <pango/pango-ot.h> #include "libnrtype/FontFactory.h" #include "libnrtype/font-instance.h" #include "util/unordered-containers.h" +#include <map> typedef INK_UNORDERED_MAP<PangoFontDescription*, font_instance*, font_descr_hash, font_descr_equal> FaceMapType; @@ -591,7 +593,23 @@ font_instance* font_factory::FaceFromFontSpecification(char const *fontSpecifica return font; } +void dump_tag( guint32 *tag, Glib::ustring prefix = "" ) { + std::cout << prefix + << ((char)((*tag & 0xff000000)>>24)) + << ((char)((*tag & 0x00ff0000)>>16)) + << ((char)((*tag & 0x0000ff00)>>8)) + << ((char)((*tag & 0x000000ff)>>0)) + << std::endl; +} +Glib::ustring extract_tag( guint32 *tag ) { + Glib::ustring tag_name; + tag_name += ((char)((*tag & 0xff000000)>>24)); + tag_name += ((char)((*tag & 0x00ff0000)>>16)); + tag_name += ((char)((*tag & 0x0000ff00)>>8)); + tag_name += ((char)((*tag & 0x000000ff)>>0)); + return tag_name; +} font_instance *font_factory::Face(PangoFontDescription *descr, bool canFail) { @@ -657,6 +675,76 @@ font_instance *font_factory::Face(PangoFontDescription *descr, bool canFail) pango_font_description_free(descr); } } + + // Extract which OpenType tables are in the font. We'll make a list of all tables + // regardless of which script and langauge they are in. These functions are deprecated but + // will eventually be replaced by newer functions (according to Behdad). + PangoOTInfo* info = pango_ot_info_get( res->theFace ); + + PangoOTTag* scripts = pango_ot_info_list_scripts( info, PANGO_OT_TABLE_GSUB ); + // std::cout << " scripts: " << std::endl; + for( unsigned i = 0; scripts[i] != 0; ++i ) { + // dump_tag( &scripts[i], " " ); + + guint script_index = -1; + if( pango_ot_info_find_script( info, PANGO_OT_TABLE_GSUB, scripts[i], &script_index )) { + + PangoOTTag* languages = + pango_ot_info_list_languages( info, PANGO_OT_TABLE_GSUB, script_index, NULL); + // if( languages[0] != 0 ) + // std::cout << " languages: " << std::endl; + + for( unsigned j = 0; languages[j] != 0; ++j ) { + // dump_tag( &languages[j], " lang: "); + + guint language_index = -1; + if( pango_ot_info_find_language(info, PANGO_OT_TABLE_GSUB, script_index, languages[j], &language_index, NULL)) { + + PangoOTTag* features = + pango_ot_info_list_features( info, PANGO_OT_TABLE_GSUB, 0, i, j ); + if( features[0] != 0 ) + // std::cout << " features: " << std::endl; + + for( unsigned k = 0; features[k] != 0; ++k ) { + // dump_tag( &features[k], " feature: "); + ++(res->openTypeTables[ extract_tag(&features[k])]); + } + g_free( features ); + } else { + // std::cout << " No languages defined" << std::endl; + PangoOTTag* features = + pango_ot_info_list_features( info, PANGO_OT_TABLE_GSUB, 0, i, PANGO_OT_DEFAULT_LANGUAGE ); + // if( features[0] != 0 ) + // std::cout << " default features: " << std::endl; + + for( unsigned k = 0; features[k] != 0; ++k ) { + // dump_tag( &features[k], " feature: " ); + ++(res->openTypeTables[ extract_tag(&features[k])]); + } + g_free( features ); + } + } + g_free( languages ); + } else { + // std::cout << " No scripts defined! " << std::endl; + } + } + g_free( scripts ); + + PangoOTTag* features = + pango_ot_info_list_features( info, PANGO_OT_TABLE_GSUB, 0, 0, PANGO_OT_DEFAULT_LANGUAGE ); + // if( features[0] != 0 ) + // std::cout << " DFTL DFTL features: " << std::endl; + for( unsigned i = 0; features[i] != 0; ++i ) { + // dump_tag( &features[i], " feature: " ); + ++(res->openTypeTables[ extract_tag(&features[i])]); + } + // std::map<Glib::ustring,int>::iterator it; + // for( it = res->openTypeTables.begin(); it != res->openTypeTables.end(); ++it) { + // std::cout << "Table: " << it->first << " Occurances: " << it->second << std::endl; + // } + g_free( features ); + } else { // already here res = loadedFaces[descr]; diff --git a/src/libnrtype/FontFactory.h b/src/libnrtype/FontFactory.h index bd5a4460c..4c8c2cb29 100644 --- a/src/libnrtype/FontFactory.h +++ b/src/libnrtype/FontFactory.h @@ -68,7 +68,7 @@ public: }; // Map type for gathering UI family and style names -typedef std::map<Glib::ustring, std::list<StyleNames> > FamilyToStylesMap; +// typedef std::map<Glib::ustring, std::list<StyleNames> > FamilyToStylesMap; class font_factory { public: diff --git a/src/libnrtype/Layout-TNG-Compute.cpp b/src/libnrtype/Layout-TNG-Compute.cpp index beff3734b..d81e1b6b4 100644 --- a/src/libnrtype/Layout-TNG-Compute.cpp +++ b/src/libnrtype/Layout-TNG-Compute.cpp @@ -958,6 +958,10 @@ void Layout::Calculator::ParagraphInfo::free() */ void Layout::Calculator::_buildPangoItemizationForPara(ParagraphInfo *para) const { + TRACE(("pango version string: %s\n", pango_version_string() )); +#if PANGO_VERSION_CHECK(1,37,1) + TRACE((" ... compiled for font features\n")); +#endif Glib::ustring para_text; PangoAttrList *attributes_list; unsigned input_index; @@ -986,9 +990,21 @@ void Layout::Calculator::_buildPangoItemizationForPara(ParagraphInfo *para) con PangoAttribute *attribute_font_description = pango_attr_font_desc_new(font->descr); attribute_font_description->start_index = para_text.bytes(); + +#if PANGO_VERSION_CHECK(1,37,1) + PangoAttribute *attribute_font_features = + pango_attr_font_features_new( text_source->style->getFontFeatureString().c_str()); + attribute_font_features->start_index = para_text.bytes(); +#endif para_text.append(&*text_source->text_begin.base(), text_source->text_length); // build the combined text attribute_font_description->end_index = para_text.bytes(); pango_attr_list_insert(attributes_list, attribute_font_description); + +#if PANGO_VERSION_CHECK(1,37,1) + attribute_font_features->end_index = para_text.bytes(); + pango_attr_list_insert(attributes_list, attribute_font_features); +#endif + // ownership of attribute is assumed by the list font->Unref(); } diff --git a/src/libnrtype/Layout-TNG-OutIter.cpp b/src/libnrtype/Layout-TNG-OutIter.cpp index c9c318960..707897f50 100644 --- a/src/libnrtype/Layout-TNG-OutIter.cpp +++ b/src/libnrtype/Layout-TNG-OutIter.cpp @@ -788,12 +788,12 @@ bool Layout::iterator::prevLineCursor(int n) { if (!_cursor_moving_vertically) beginCursorUpDown(); - unsigned line_index; + int line_index; if (_char_index == _parent_layout->_characters.size()) line_index = _parent_layout->_lines.size() - 1; else line_index = _parent_layout->_characters[_char_index].chunk(_parent_layout).in_line; - if (line_index == 0) + if (line_index <= 0) return false; // nowhere to go else n = MIN (n, static_cast<int>(line_index)); diff --git a/src/libnrtype/Layout-TNG.h b/src/libnrtype/Layout-TNG.h index e91c32ebe..26db1fad9 100644 --- a/src/libnrtype/Layout-TNG.h +++ b/src/libnrtype/Layout-TNG.h @@ -701,7 +701,11 @@ private: /** The overall block-progression of the whole flow. */ inline Direction _blockProgression() const - {return static_cast<InputStreamTextSource*>(_input_stream.front())->styleGetBlockProgression();} + { + if(!_input_stream.empty()) + return static_cast<InputStreamTextSource*>(_input_stream.front())->styleGetBlockProgression(); + return TOP_TO_BOTTOM; + } /** so that LEFT_TO_RIGHT == RIGHT_TO_LEFT but != TOP_TO_BOTTOM */ static bool _directions_are_orthogonal(Direction d1, Direction d2); diff --git a/src/libnrtype/font-instance.h b/src/libnrtype/font-instance.h index 2c7d1ce46..5a71e353b 100644 --- a/src/libnrtype/font-instance.h +++ b/src/libnrtype/font-instance.h @@ -36,6 +36,9 @@ public: int nbGlyph, maxGlyph; font_glyph* glyphs; + // Map of OpenType tables found in font (convert to std::set?) + std::map<Glib::ustring, int> openTypeTables; + font_instance(void); virtual ~font_instance(void); @@ -68,6 +71,8 @@ public: private: void FreeTheFace(); + // Temp: make public +public: #ifdef USE_PANGO_WIN32 HFONT theFace; #else diff --git a/src/libnrtype/font-lister.cpp b/src/libnrtype/font-lister.cpp index a6ab3b239..70374864a 100644 --- a/src/libnrtype/font-lister.cpp +++ b/src/libnrtype/font-lister.cpp @@ -335,7 +335,7 @@ Glib::ustring FontLister::system_fontspec(Glib::ustring fontspec) PangoFontDescription *descr = pango_font_description_from_string(fontspec.c_str()); font_instance *res = (font_factory::Default())->Face(descr); - if (res->pFont) { + if (res && res->pFont) { PangoFontDescription *nFaceDesc = pango_font_describe(res->pFont); out = sp_font_description_get_family(nFaceDesc); } diff --git a/src/libuemf/CMakeLists.txt b/src/libuemf/CMakeLists.txt index f5a97f212..9e6b68994 100644 --- a/src/libuemf/CMakeLists.txt +++ b/src/libuemf/CMakeLists.txt @@ -4,6 +4,7 @@ set(libuemf_SRC uemf.c uemf_endian.c uemf_print.c + uemf_safe.c uemf_utf.c uwmf.c uwmf_endian.c @@ -17,6 +18,7 @@ set(libuemf_SRC uemf.h uemf_endian.h uemf_print.h + uemf_safe.h uemf_utf.h uwmf.h uwmf_endian.h diff --git a/src/libuemf/Makefile_insert b/src/libuemf/Makefile_insert index 004c56dfc..427a0e80e 100644 --- a/src/libuemf/Makefile_insert +++ b/src/libuemf/Makefile_insert @@ -12,6 +12,8 @@ libuemf_libuemf_a_SOURCES = \ libuemf/uemf_endian.h \ libuemf/uemf_print.c \ libuemf/uemf_print.h \ + libuemf/uemf_safe.c \ + libuemf/uemf_safe.h \ libuemf/uemf_utf.c \ libuemf/uemf_utf.h \ libuemf/uwmf.c \ diff --git a/src/libuemf/README b/src/libuemf/README index cfa322178..a3dff9b4c 100644 --- a/src/libuemf/README +++ b/src/libuemf/README @@ -1,22 +1,25 @@ Overview: -libUEMF is a portable C99 implementation for reading/writing Enhanced Metafile (EMF) -and Windows Metafile (WMF) files. libUEMF avoids collisions with with Microsoft defined -functions and values, so portable programs which use it, and have a Windows version, do -not require any conditional logic to separate the native GDI support from the WMF/EMF support -proviced by libUEMF. To accomplish this libUEMF does not implement GDI calls. Instead, -for each WMR/EMR record type, and each object type incorporated into such a record, it provides -corresponding *_set, *_print, and *_swap functions. (For WMF there are also *_get functions, see below.) -For example, for the U_EMRBITBLT record there are corresponding functions: -U_EMRBITBLT_set, U_EMRBITBLT_print, and U_EMRBITBLT_swap. A few additional functions are provided for -assembling the EMF in memory, debugging, and converting the EMF file to/from Little Endian representation. -(EMF files' internal data representation is always Little Endian.) - -This code has been tested on 32 bit Ubuntu (LE), 32 bit Mingw, 64 bit Manriva, and 64 bit Solaris (BE). +libUEMF is a portable C99 implementation for reading/writing Enhanced Metafile (EMF), +Enhanced Metafile Format Plus (PMF), and Windows Metafile (WMF) files. libUEMF +avoids collisions with Microsoft defined functions and values, so portable programs +which use it and have a Windows version, do not require any conditional logic to +separate the native GDI support from the WMF/EMF/PMF support proviced by libUEMF. To +accomplish this libUEMF does not implement GDI calls. Instead, for each WMR/EMR/PMR +record type, and each object type incorporated into such a record, it provides +corresponding *_set, *_print, and *_swap functions. For PMF and WMF there are also +*_get functions, see below. For example, for the U_EMRBITBLT record there are +corresponding functions: U_EMRBITBLT_set, U_EMRBITBLT_print, and U_EMRBITBLT_swap. A +few additional functions are provided for assembling the EMF in memory, debugging, and +converting the EMF file to/from Little Endian representation. (EMF files' internal +data representation is always Little Endian.) + +This code has been tested on 32 bit Ubuntu (LE), 32 bit Mingw, 64 bit Mageia, and 64 +bit Solaris (BE). libUEMF is released under the GPL 2 license, read the file 'COPYING' for more information -Version 0.1.10, released January 15, 2014. +Version 0.2.1, released April 23, 2015. To report bugs or provide feedback send email to David Mathog, mathog@caltech.edu. @@ -76,7 +79,14 @@ uemf_endian.c Contains the *_swap functions needed to rearrange bytes betwee uemf_endian.h Prototype for U_emf_endian() and definitions for Endian type of the local machine. - +uemf_safe.c Contains the *_safe functions for EMF records, which verify that all + offsets and counts stay within the declared size of a record. Also checks that + core record sizes are sane. U_emf_record_safe() is the only _safe function which + user code should call directly, and then ONLY after a previous call to + U_emf_record_sizeok(), which is in the endian file. + +uemf_safe.h Prototype for U_emf_record_safe(). + . upmf.c Contains the *_set and *_get functions needed to construct or read an EMF+ file. Also contains auxilliary functions for debugging and constructing EMF+ files in memory. @@ -366,9 +376,69 @@ History (Note, version numbers in files represent the libUEMF release where it was last modified, so not all files will show the same version numbers in each release.) +0.2.1.2015_04_23 + Bug in safety check on EMREXTCREATEFONTINDIRECTW because it had alternative "standard" record sizes. + Changed warnings on unimplemented EMF record types encounterd in swap or safe from stdout to stderr. + Added memory checking for WMF polyline and polygon records, for the variable part and some others. + Note: U_WMRCREATEREGION_get does not check that the variable part stays within the record. Current + implementation seems to be broken since it does not show up in XP preview. +0.2.0 2015_03_20 + Added UEMF _safe functions to better handle corrupt records, where variable sizes fields might + have been specified to read past the end of memory. These are records with offsets, arrays + with counts, and bitmaps. Also any record which specifies a size smaller than the minimum + for that record type. + Added similar code for EMF+. + These changed the API so the minor version number was bumped by 1. + +0.1.18 2015_01_15 + Pierre-Francois Carpentier sent some EMF examples which used U_EMR_EXTSELECTCLIPRGN, which had + not previously been encountered and showed that the handling of regions was broken. + Added tests for U_EMRFILLRGN, U_EMRFRAMERGN, U_EMRFRAMERGN, U_EMREXTSELECTCLIPRGN to testbed_emf. + +0.1.18 2014_04_28 + Fixed typo in testbed_wmf.c. "include,include" in one place should have been + "exclude,exclude". + +0.1.17 2014_04_25 + Added text clipping tests to testbed_emf.c, testbed_wmf.c, and testbed_pmf.c. + Added option to omit clipping tests in testbed's. + +0.1.16 2014_04_14 + Fixed bug in U_WMRRESTOREDC_set. + Added clipping tests to testbed_wmf.c. + +0.1.15 2014_04_04 + Changed record flags for U_EMRSELECTCLIPPATH record, it consumes a path but does not ALTER (which + forced a premature draw.) + Added U_EMROFFSETCLIPRGN test to testbed_emf.c. + Changed location on dist drawing where clipping appears. + +0.1.14 2014_03_27 + Fixed bug, in U_PMF_RECTN_set() in upmf.c. Never tested. + Fixed a few potential bugs in upmf.c if a null pointer was passed for + certain function arguments. (Previously unhandled cases.) + Fixed bug, operations setting variables that are never read along those + execution paths: upmf_print.c, uemf.c, uwmf.c, uemf_endian.cm upmf.c. + Fixed potential (but very unlikely) memory leaks in upmf.c and uemf_utf.c. + Added test of U_PMF_RECTN_set to testbed_pmf.c. + Changed U_wmr_names() and U_wmr_escnames() to const char* (from char*). + Changed method for suppressing unused parameter warnings. + + +0.1.13 2014-03-21 + Fixed bug, cutemf was messing up the object count (pens, brushes, etc.). + Added cutemf can now take ranges of lines. + Added testbed_emf generates clipping records for rect and path (but not region). + +0.1.12 2014-02-14 + Documentation change, U_WMRCREATEPATTERNBRUSH is obsolete. + Changed wmf_finish() so that it accurately reflects the largest number of objects used, + previously it showed the number of appends, which was much larger. + 0.1.11 2014-01-29 Fixed bug in uwmf.c (wrong minimum record size on U_WMRTEXTOUT) Fixed bug in uwmf.c (U_WMRCREATEPATTERNBRUSH not right) + Fixed bug in uwmf_print.c (U_WMRTEXTOUT_print, x,y were reversed) Added error handling to uemf_utf.c for cases where src is a null pointer. Added a test of createpatternbrush to testlib_wmf diff --git a/src/libuemf/uemf.c b/src/libuemf/uemf.c index 1d417dece..3180c757c 100644 --- a/src/libuemf/uemf.c +++ b/src/libuemf/uemf.c @@ -16,11 +16,11 @@ /* File: uemf.c -Version: 0.0.28 -Date: 04-APR-2014 +Version: 0.0.30 +Date: 20-MAR-2015 Author: David Mathog, Biology Division, Caltech email: mathog@caltech.edu -Copyright: 2014 David Mathog and California Institute of Technology (Caltech) +Copyright: 2015 David Mathog and California Institute of Technology (Caltech) */ #ifdef __cplusplus @@ -199,7 +199,7 @@ definitions are not needed in end user code, so they are here rather than in uem // this one may also be used A=Msk,B=MskBmi and F=cbMsk #define SET_CB_FROM_PXBMI(A,B,C,D,E,F) /* A=Px, B=Bmi, C=cbImage, D=cbImage4, E=cbBmi, F=cbPx */ \ if(A){\ - if(!B)return(NULL); /* size is derived from U_BIMAPINFO, but NOT from its size field, go figure*/ \ + if(!B)return(NULL); /* size is derived from U_BITMAPINFO, but NOT from its size field, go figure*/ \ C = F;\ D = UP4(C); /* pixel array might not be a multiples of 4 bytes*/ \ E = sizeof(U_BITMAPINFOHEADER) + 4 * get_real_color_count((const char *) &(B->bmiHeader)); /* bmiheader + colortable*/ \ @@ -851,7 +851,7 @@ int get_real_color_icount( \brief Get the DIB parameters from the BMI of the record for use by DBI_to_RGBA() \return BI_Compression Enumeration. For anything other than U_BI_RGB values other than px may not be valid. - \param pEmr pointer to EMR record that has a U_BITMAPINFO and bitmap + \param record pointer to EMR record that has a U_BITMAPINFO and bitmap \param offBitsSrc Offset to the bitmap \param offBmiSrc Offset to the U_BITMAPINFO \param px pointer to DIB pixel array in pEmr @@ -863,7 +863,7 @@ int get_real_color_icount( \param invert If DIB rows are in opposite order from RGBA rows */ int get_DIB_params( - void *pEmr, + const char *record, uint32_t offBitsSrc, uint32_t offBmiSrc, const char **px, @@ -875,7 +875,7 @@ int get_DIB_params( uint32_t *invert ){ uint32_t bic; - PU_BITMAPINFO Bmi = (PU_BITMAPINFO)((char *)pEmr + offBmiSrc); + PU_BITMAPINFO Bmi = (PU_BITMAPINFO)(record + offBmiSrc); PU_BITMAPINFOHEADER Bmih = &(Bmi->bmiHeader); /* if biCompression is not U_BI_RGB some or all of the following might not hold real values */ bic = Bmih->biCompression; @@ -903,7 +903,7 @@ int get_DIB_params( *numCt = Bmih->biSizeImage; *ct = NULL; } - *px = (char *)((char *)pEmr + offBitsSrc); + *px = record + offBitsSrc; return(bic); } @@ -1611,6 +1611,26 @@ U_RECTL rectl_set( } /** + \brief Set rect and rectl objects from Upper Left and Lower Right corner points. + \param array array of rectangles + \param index array entry to fill, numbered from 0 + \param ul upper left corner of rectangle + \param lr lower right corner of rectangle +*/ +void rectli_set( + PU_RECTL array, + int index, + U_POINTL ul, + U_POINTL lr + ){ + PU_RECTL rct = &(array[index]); + rct->left = ul.x; + rct->top = ul.y; + rct->right = lr.x; + rct->bottom = lr.y; +} + +/** \brief Set sizel objects with X,Y values. \param x X coordinate \param y Y coordinate @@ -2192,7 +2212,7 @@ U_RGNDATAHEADER rgndataheader_set( rdh.dwSize = U_RDH_OBJSIZE; rdh.iType = U_RDH_RECTANGLES; rdh.nCount = nCount; - rdh.nRgnSize = nCount * sizeof(U_RECTL); // Size in bytes of retangle array + rdh.nRgnSize = nCount * sizeof(U_RECTL); // Size in bytes of rectangle array rdh.rclBounds = rclBounds; return(rdh); } @@ -2857,7 +2877,7 @@ by end user code and to further that end prototypes are NOT provided and they ar // Functions with the same form starting with U_EMRPOLYBEZIER_set -char *U_EMR_CORE1(uint32_t iType, U_RECTL rclBounds, const uint32_t cptl, const U_POINTL *points){ +char *U_EMR_CORE1_set(uint32_t iType, U_RECTL rclBounds, const uint32_t cptl, const U_POINTL *points){ char *record; int cbPoints; int irecsize; @@ -2876,7 +2896,7 @@ char *U_EMR_CORE1(uint32_t iType, U_RECTL rclBounds, const uint32_t cptl, const } // Functions with the same form starting with U_EMR_POLYPOLYLINE -char *U_EMR_CORE2(uint32_t iType, U_RECTL rclBounds, const uint32_t nPolys, const uint32_t *aPolyCounts,const uint32_t cptl, const U_POINTL *points){ +char *U_EMR_CORE2_set(uint32_t iType, U_RECTL rclBounds, const uint32_t nPolys, const uint32_t *aPolyCounts,const uint32_t cptl, const U_POINTL *points){ char *record; int cbPolys,cbPoints,off; int irecsize; @@ -2899,7 +2919,7 @@ char *U_EMR_CORE2(uint32_t iType, U_RECTL rclBounds, const uint32_t nPolys, cons } // Functions with the same form starting with U_EMR_SETMAPMODE_set -char *U_EMR_CORE3(uint32_t iType, uint32_t iMode){ +char *U_EMR_CORE3_set(uint32_t iType, uint32_t iMode){ char *record; int irecsize; @@ -2914,7 +2934,7 @@ char *U_EMR_CORE3(uint32_t iType, uint32_t iMode){ } // Functions taking a single U_RECT or U_RECTL, starting with U_EMRELLIPSE_set, also U_EMRFILLPATH, -char *U_EMR_CORE4(uint32_t iType, U_RECTL rclBox){ +char *U_EMR_CORE4_set(uint32_t iType, U_RECTL rclBox){ char *record; int irecsize; @@ -2930,7 +2950,7 @@ char *U_EMR_CORE4(uint32_t iType, U_RECTL rclBox){ } // Functions with the same form starting with U_EMRSETMETARGN_set -char *U_EMR_CORE5(uint32_t iType){ +char *U_EMR_CORE5_set(uint32_t iType){ char *record; int irecsize = 8; @@ -2943,7 +2963,7 @@ char *U_EMR_CORE5(uint32_t iType){ } // Functions with the same form starting with U_EMRPOLYBEZIER16_set -char *U_EMR_CORE6(uint32_t iType, U_RECTL rclBounds, const uint32_t cpts, const U_POINT16 *points){ +char *U_EMR_CORE6_set(uint32_t iType, U_RECTL rclBounds, const uint32_t cpts, const U_POINT16 *points){ char *record; int cbPoints,cbPoints4,off; int irecsize; @@ -2970,7 +2990,7 @@ char *U_EMR_CORE6(uint32_t iType, U_RECTL rclBounds, const uint32_t cpts, const // Functions that take a single struct argument which contains two uint32_t, starting with U_EMRSETWINDOWEXTEX_set // these all pass two 32 bit ints and are cast by the caller to U_PAIR -char *U_EMR_CORE7(uint32_t iType, U_PAIR pair){ +char *U_EMR_CORE7_set(uint32_t iType, U_PAIR pair){ char *record; int irecsize = sizeof(U_EMRGENERICPAIR); @@ -2984,7 +3004,7 @@ char *U_EMR_CORE7(uint32_t iType, U_PAIR pair){ } // For U_EMREXTTEXTOUTA and U_EMREXTTEXTOUTW -char *U_EMR_CORE8( +char *U_EMR_CORE8_set( uint32_t iType, U_RECTL rclBounds, // Bounding rectangle in device units uint32_t iGraphicsMode, // Graphics mode Enumeration @@ -3033,7 +3053,7 @@ char *U_EMR_CORE8( } // Functions that take a rect and a pair of points, starting with U_EMRARC_set -char *U_EMR_CORE9(uint32_t iType, U_RECTL rclBox, U_POINTL ptlStart, U_POINTL ptlEnd){ +char *U_EMR_CORE9_set(uint32_t iType, U_RECTL rclBox, U_POINTL ptlStart, U_POINTL ptlEnd){ char *record; int irecsize = sizeof(U_EMRARC); @@ -3049,7 +3069,7 @@ char *U_EMR_CORE9(uint32_t iType, U_RECTL rclBox, U_POINTL ptlStart, U_POINTL pt } // Functions with the same form starting with U_EMR_POLYPOLYLINE16 -char *U_EMR_CORE10(uint32_t iType, U_RECTL rclBounds, const uint32_t nPolys, const uint32_t *aPolyCounts,const uint32_t cpts, const U_POINT16 *points){ +char *U_EMR_CORE10_set(uint32_t iType, U_RECTL rclBounds, const uint32_t nPolys, const uint32_t *aPolyCounts,const uint32_t cpts, const U_POINT16 *points){ char *record; int cbPoints,cbPolys,off; int irecsize; @@ -3072,25 +3092,27 @@ char *U_EMR_CORE10(uint32_t iType, U_RECTL rclBounds, const uint32_t nPolys, con } // common code for U_EMRINVERTRGN and U_EMRPAINTRGN, -char *U_EMR_CORE11(uint32_t iType, PU_RGNDATA RgnData){ +char *U_EMR_CORE11_set(uint32_t iType, PU_RGNDATA RgnData){ char *record; int irecsize; - int cbRgns,cbRgns4,off; + int cbRgns,cbRgns4,rds,rds4,off; if(!RgnData)return(NULL); cbRgns = ((PU_RGNDATAHEADER) RgnData)->nRgnSize; cbRgns4 = UP4(cbRgns); - irecsize = sizeof(U_EMRINVERTRGN) + cbRgns4 - sizeof(U_RGNDATAHEADER); // core + array - overlap - record = malloc(irecsize); + rds = sizeof(U_RGNDATAHEADER) + cbRgns; + rds4 = UP4(rds); + irecsize = sizeof(U_EMRINVERTRGN) - sizeof(U_RECTL) + cbRgns4; // core + array - overlap of one rectL + record = malloc(irecsize); if(record){ ((PU_EMR) record)->iType = iType; ((PU_EMR) record)->nSize = irecsize; ((PU_EMRINVERTRGN) record)->rclBounds = ((PU_RGNDATAHEADER) RgnData)->rclBounds; - ((PU_EMRINVERTRGN) record)->cbRgnData = cbRgns; - off = irecsize - cbRgns4; - memcpy(record + off, RgnData, cbRgns); - off += cbRgns; - if(cbRgns < cbRgns4){ memset(record + off,0, cbRgns4 - cbRgns); } // clear any unused bytes + ((PU_EMRINVERTRGN) record)->cbRgnData = rds; + off = sizeof(U_EMRINVERTRGN) - sizeof(U_RGNDATA); + memcpy(record + off, RgnData, rds); + off += rds; + if(rds < rds4){ memset(record + off,0, rds4 - rds); } // clear any unused bytes } return(record); } @@ -3274,7 +3296,7 @@ char *U_EMRPOLYBEZIER_set( const uint32_t cptl, const U_POINTL *points ){ - return(U_EMR_CORE1(U_EMR_POLYBEZIER, rclBounds, cptl, points)); + return(U_EMR_CORE1_set(U_EMR_POLYBEZIER, rclBounds, cptl, points)); } // U_EMRPOLYGON_set 3 @@ -3290,7 +3312,7 @@ char *U_EMRPOLYGON_set( const uint32_t cptl, const U_POINTL *points ){ - return(U_EMR_CORE1(U_EMR_POLYGON, rclBounds, cptl, points)); + return(U_EMR_CORE1_set(U_EMR_POLYGON, rclBounds, cptl, points)); } // U_EMRPOLYLINE_set 4 @@ -3306,7 +3328,7 @@ char *U_EMRPOLYLINE_set( const uint32_t cptl, const U_POINTL *points ){ - return(U_EMR_CORE1(U_EMR_POLYLINE, rclBounds, cptl, points)); + return(U_EMR_CORE1_set(U_EMR_POLYLINE, rclBounds, cptl, points)); } // U_EMRPOLYBEZIERTO_set 5 @@ -3322,7 +3344,7 @@ char *U_EMRPOLYBEZIERTO_set( const uint32_t cptl, const U_POINTL *points ){ - return(U_EMR_CORE1(U_EMR_POLYBEZIERTO, rclBounds, cptl, points)); + return(U_EMR_CORE1_set(U_EMR_POLYBEZIERTO, rclBounds, cptl, points)); } // U_EMRPOLYLINETO_set 6 @@ -3338,7 +3360,7 @@ char *U_EMRPOLYLINETO_set( const uint32_t cptl, const U_POINTL *points ){ - return(U_EMR_CORE1(U_EMR_POLYLINETO, rclBounds, cptl, points)); + return(U_EMR_CORE1_set(U_EMR_POLYLINETO, rclBounds, cptl, points)); } // U_EMRPOLYPOLYLINE_set 7 @@ -3358,7 +3380,7 @@ char *U_EMRPOLYPOLYLINE_set( const uint32_t cptl, const U_POINTL *points ){ - return(U_EMR_CORE2(U_EMR_POLYPOLYLINE, rclBounds, nPolys, aPolyCounts,cptl, points)); + return(U_EMR_CORE2_set(U_EMR_POLYPOLYLINE, rclBounds, nPolys, aPolyCounts,cptl, points)); } // U_EMRPOLYPOLYGON_set 8 @@ -3378,7 +3400,7 @@ char *U_EMRPOLYPOLYGON_set( const uint32_t cptl, const U_POINTL *points ){ - return(U_EMR_CORE2(U_EMR_POLYPOLYGON, rclBounds, nPolys, aPolyCounts,cptl, points)); + return(U_EMR_CORE2_set(U_EMR_POLYPOLYGON, rclBounds, nPolys, aPolyCounts,cptl, points)); } // U_EMRSETWINDOWEXTEX_set 9 @@ -3393,7 +3415,7 @@ char *U_EMRSETWINDOWEXTEX_set( U_PAIR temp; temp.x = szlExtent.cx; temp.y = szlExtent.cy; - return(U_EMR_CORE7(U_EMR_SETWINDOWEXTEX, temp)); + return(U_EMR_CORE7_set(U_EMR_SETWINDOWEXTEX, temp)); } // U_EMRSETWINDOWORGEX_set 10 @@ -3405,7 +3427,7 @@ char *U_EMRSETWINDOWEXTEX_set( char *U_EMRSETWINDOWORGEX_set( const U_POINTL ptlOrigin ){ - return(U_EMR_CORE7(U_EMR_SETWINDOWORGEX, ptlOrigin)); // U_PAIR and U_POINTL are the same thing + return(U_EMR_CORE7_set(U_EMR_SETWINDOWORGEX, ptlOrigin)); // U_PAIR and U_POINTL are the same thing } // U_EMRSETVIEWPORTEXTEX_set 11 @@ -3420,7 +3442,7 @@ char *U_EMRSETVIEWPORTEXTEX_set( U_PAIR temp; temp.x = szlExtent.cx; temp.y = szlExtent.cy; - return(U_EMR_CORE7(U_EMR_SETVIEWPORTEXTEX, temp)); + return(U_EMR_CORE7_set(U_EMR_SETVIEWPORTEXTEX, temp)); } // U_EMRSETVIEWPORTORGEX_set 12 @@ -3432,7 +3454,7 @@ char *U_EMRSETVIEWPORTEXTEX_set( char *U_EMRSETVIEWPORTORGEX_set( const U_POINTL ptlOrigin ){ - return(U_EMR_CORE7(U_EMR_SETVIEWPORTORGEX, ptlOrigin)); // U_PAIR and U_POINTL are the same thing + return(U_EMR_CORE7_set(U_EMR_SETVIEWPORTORGEX, ptlOrigin)); // U_PAIR and U_POINTL are the same thing } // U_EMRSETBRUSHORGEX_set 13 @@ -3444,7 +3466,7 @@ char *U_EMRSETVIEWPORTORGEX_set( char *U_EMRSETBRUSHORGEX_set( const U_POINTL ptlOrigin ){ - return(U_EMR_CORE7(U_EMR_SETBRUSHORGEX, *((PU_PAIR) & ptlOrigin))); + return(U_EMR_CORE7_set(U_EMR_SETBRUSHORGEX, *((PU_PAIR) & ptlOrigin))); } // U_EMREOF_set 14 @@ -3544,7 +3566,7 @@ char *U_EMRSETMAPPERFLAGS_set(void){ char *U_EMRSETMAPMODE_set( const uint32_t iMode ){ - return(U_EMR_CORE3(U_EMR_SETMAPMODE, iMode)); + return(U_EMR_CORE3_set(U_EMR_SETMAPMODE, iMode)); } // U_EMRSETBKMODE_set 18 @@ -3556,7 +3578,7 @@ char *U_EMRSETMAPMODE_set( char *U_EMRSETBKMODE_set( const uint32_t iMode ){ - return(U_EMR_CORE3(U_EMR_SETBKMODE, iMode)); + return(U_EMR_CORE3_set(U_EMR_SETBKMODE, iMode)); } // U_EMRSETPOLYFILLMODE_set 19 @@ -3568,7 +3590,7 @@ char *U_EMRSETBKMODE_set( char *U_EMRSETPOLYFILLMODE_set( const uint32_t iMode ){ - return(U_EMR_CORE3(U_EMR_SETPOLYFILLMODE, iMode)); + return(U_EMR_CORE3_set(U_EMR_SETPOLYFILLMODE, iMode)); } // U_EMRSETROP2_set 20 @@ -3580,7 +3602,7 @@ char *U_EMRSETPOLYFILLMODE_set( char *U_EMRSETROP2_set( const uint32_t iMode ){ - return(U_EMR_CORE3(U_EMR_SETROP2, iMode)); + return(U_EMR_CORE3_set(U_EMR_SETROP2, iMode)); } // U_EMRSETSTRETCHBLTMODE_set 21 @@ -3592,7 +3614,7 @@ char *U_EMRSETROP2_set( char *U_EMRSETSTRETCHBLTMODE_set( const uint32_t iMode ){ - return(U_EMR_CORE3(U_EMR_SETSTRETCHBLTMODE, iMode)); + return(U_EMR_CORE3_set(U_EMR_SETSTRETCHBLTMODE, iMode)); } // U_EMRSETTEXTALIGN_set 22 @@ -3604,7 +3626,7 @@ char *U_EMRSETSTRETCHBLTMODE_set( char *U_EMRSETTEXTALIGN_set( const uint32_t iMode ){ - return(U_EMR_CORE3(U_EMR_SETTEXTALIGN, iMode)); + return(U_EMR_CORE3_set(U_EMR_SETTEXTALIGN, iMode)); } // U_EMRSETCOLORADJUSTMENT_set 23 @@ -3638,7 +3660,7 @@ char *U_EMRSETCOLORADJUSTMENT_set( char *U_EMRSETTEXTCOLOR_set( const U_COLORREF crColor ){ - return(U_EMR_CORE3(U_EMR_SETTEXTCOLOR, *(uint32_t *) &crColor)); + return(U_EMR_CORE3_set(U_EMR_SETTEXTCOLOR, *(uint32_t *) &crColor)); } // U_EMRSETBKCOLOR_set 25 @@ -3650,7 +3672,7 @@ char *U_EMRSETTEXTCOLOR_set( char *U_EMRSETBKCOLOR_set( const U_COLORREF crColor ){ - return(U_EMR_CORE3(U_EMR_SETBKCOLOR, *(uint32_t *) &crColor)); + return(U_EMR_CORE3_set(U_EMR_SETBKCOLOR, *(uint32_t *) &crColor)); } // U_EMROFFSETCLIPRGN_set 26 @@ -3662,7 +3684,7 @@ char *U_EMRSETBKCOLOR_set( char *U_EMROFFSETCLIPRGN_set( const U_POINTL ptl ){ - return(U_EMR_CORE7(U_EMR_OFFSETCLIPRGN, ptl)); + return(U_EMR_CORE7_set(U_EMR_OFFSETCLIPRGN, ptl)); } // U_EMRMOVETOEX_set 27 @@ -3674,7 +3696,7 @@ char *U_EMROFFSETCLIPRGN_set( char *U_EMRMOVETOEX_set( const U_POINTL ptl ){ - return(U_EMR_CORE7(U_EMR_MOVETOEX, ptl)); + return(U_EMR_CORE7_set(U_EMR_MOVETOEX, ptl)); } // U_EMRSETMETARGN_set 28 @@ -3683,7 +3705,7 @@ char *U_EMRMOVETOEX_set( \return pointer to U_EMR_SETMETARGN record, or NULL on error. */ char *U_EMRSETMETARGN_set(void){ - return(U_EMR_CORE5(U_EMR_SETMETARGN)); + return(U_EMR_CORE5_set(U_EMR_SETMETARGN)); } // U_EMREXCLUDECLIPRECT_set 29 @@ -3695,7 +3717,7 @@ char *U_EMRSETMETARGN_set(void){ char *U_EMREXCLUDECLIPRECT_set( const U_RECTL rclClip ){ - return(U_EMR_CORE4(U_EMR_EXCLUDECLIPRECT,rclClip)); + return(U_EMR_CORE4_set(U_EMR_EXCLUDECLIPRECT,rclClip)); } // U_EMRINTERSECTCLIPRECT_set 30 @@ -3707,7 +3729,7 @@ char *U_EMREXCLUDECLIPRECT_set( char *U_EMRINTERSECTCLIPRECT_set( const U_RECTL rclClip ){ - return(U_EMR_CORE4(U_EMR_INTERSECTCLIPRECT,rclClip)); + return(U_EMR_CORE4_set(U_EMR_INTERSECTCLIPRECT,rclClip)); } // U_EMRSCALEVIEWPORTEXTEX_set 31 @@ -3725,7 +3747,7 @@ char *U_EMRSCALEVIEWPORTEXTEX_set( const int32_t yNum, const int32_t yDenom ){ - return(U_EMR_CORE4(U_EMR_SCALEVIEWPORTEXTEX,(U_RECTL){xNum,xDenom,yNum,yDenom})); + return(U_EMR_CORE4_set(U_EMR_SCALEVIEWPORTEXTEX,(U_RECTL){xNum,xDenom,yNum,yDenom})); } @@ -3744,7 +3766,7 @@ char *U_EMRSCALEWINDOWEXTEX_set( const int32_t yNum, const int32_t yDenom ){ - return(U_EMR_CORE4(U_EMR_SCALEWINDOWEXTEX,(U_RECTL){xNum,xDenom,yNum,yDenom})); + return(U_EMR_CORE4_set(U_EMR_SCALEWINDOWEXTEX,(U_RECTL){xNum,xDenom,yNum,yDenom})); } // U_EMRSAVEDC_set 33 @@ -3753,7 +3775,7 @@ char *U_EMRSCALEWINDOWEXTEX_set( \return pointer to U_EMR_SAVEDC record, or NULL on error. */ char *U_EMRSAVEDC_set(void){ - return(U_EMR_CORE5(U_EMR_SAVEDC)); + return(U_EMR_CORE5_set(U_EMR_SAVEDC)); } // U_EMRRESTOREDC_set 34 @@ -3765,7 +3787,7 @@ char *U_EMRSAVEDC_set(void){ char *U_EMRRESTOREDC_set( const int32_t iRelative ){ - return(U_EMR_CORE3(U_EMR_RESTOREDC, (uint32_t) iRelative)); + return(U_EMR_CORE3_set(U_EMR_RESTOREDC, (uint32_t) iRelative)); } // U_EMRSETWORLDTRANSFORM_set 35 @@ -3952,7 +3974,7 @@ char *U_EMRANGLEARC_set( char *U_EMRELLIPSE_set( const U_RECTL rclBox ){ - return(U_EMR_CORE4(U_EMR_ELLIPSE,rclBox)); + return(U_EMR_CORE4_set(U_EMR_ELLIPSE,rclBox)); } // U_EMRRECTANGLE_set 43 @@ -3964,7 +3986,7 @@ char *U_EMRELLIPSE_set( char *U_EMRRECTANGLE_set( const U_RECTL rclBox ){ - return(U_EMR_CORE4(U_EMR_RECTANGLE,rclBox)); + return(U_EMR_CORE4_set(U_EMR_RECTANGLE,rclBox)); } // U_EMRROUNDRECT_set 44 @@ -4005,7 +4027,7 @@ char *U_EMRARC_set( const U_POINTL ptlStart, const U_POINTL ptlEnd ){ - return(U_EMR_CORE9(U_EMR_ARC,rclBox, ptlStart, ptlEnd)); + return(U_EMR_CORE9_set(U_EMR_ARC,rclBox, ptlStart, ptlEnd)); } // U_EMRCHORD_set 46 @@ -4021,7 +4043,7 @@ char *U_EMRCHORD_set( const U_POINTL ptlStart, const U_POINTL ptlEnd ){ - return(U_EMR_CORE9(U_EMR_CHORD,rclBox, ptlStart, ptlEnd)); + return(U_EMR_CORE9_set(U_EMR_CHORD,rclBox, ptlStart, ptlEnd)); } // U_EMRPIE_set 47 @@ -4037,7 +4059,7 @@ char *U_EMRPIE_set( const U_POINTL ptlStart, const U_POINTL ptlEnd ){ - return(U_EMR_CORE9(U_EMR_PIE,rclBox, ptlStart, ptlEnd)); + return(U_EMR_CORE9_set(U_EMR_PIE,rclBox, ptlStart, ptlEnd)); } // U_EMRSELECTPALETTE_set 48 @@ -4049,7 +4071,7 @@ char *U_EMRPIE_set( char *U_EMRSELECTPALETTE_set( const uint32_t ihPal ){ - return(U_EMR_CORE3(U_EMR_SELECTPALETTE, ihPal)); + return(U_EMR_CORE3_set(U_EMR_SELECTPALETTE, ihPal)); } // U_EMRCREATEPALETTE_set 49 @@ -4124,7 +4146,7 @@ char *U_EMRRESIZEPALETTE_set( const uint32_t ihPal, const uint32_t cEntries ){ - return(U_EMR_CORE7(U_EMR_RESIZEPALETTE, (U_PAIR){ihPal,cEntries})); + return(U_EMR_CORE7_set(U_EMR_RESIZEPALETTE, (U_PAIR){ihPal,cEntries})); } // U_EMRREALIZEPALETTE_set 52 @@ -4133,7 +4155,7 @@ char *U_EMRRESIZEPALETTE_set( \return pointer to U_EMR_REALIZEPALETTE record, or NULL on error. */ char *U_EMRREALIZEPALETTE_set(void){ - return(U_EMR_CORE5(U_EMR_REALIZEPALETTE)); + return(U_EMR_CORE5_set(U_EMR_REALIZEPALETTE)); } // U_EMREXTFLOODFILL_set 53 @@ -4173,7 +4195,7 @@ char *U_EMREXTFLOODFILL_set( char *U_EMRLINETO_set( const U_POINTL ptl ){ - return(U_EMR_CORE7(U_EMR_LINETO, ptl)); + return(U_EMR_CORE7_set(U_EMR_LINETO, ptl)); } // U_EMRARCTO_set 55 @@ -4192,7 +4214,7 @@ char *U_EMRARCTO_set( U_POINTL ptlStart, U_POINTL ptlEnd ){ - return(U_EMR_CORE9(U_EMR_ARCTO,rclBox, ptlStart, ptlEnd)); + return(U_EMR_CORE9_set(U_EMR_ARCTO,rclBox, ptlStart, ptlEnd)); } // U_EMRPOLYDRAW_set 56 @@ -4216,7 +4238,7 @@ char *U_EMRPOLYDRAW_set( if(!cptl || !aptl || !abTypes)return(NULL); cbPoints = cptl * sizeof(U_POINTL); // space for aptl - cbAbTypes = cptl; // number of abTypes (also size, 1 byte each) + cbAbTypes = cptl; // number of abTypes (same array size, 1 byte each) cbAbTypes4 = UP4(cbAbTypes); // space for abTypes irecsize = sizeof(U_EMRPOLYDRAW) + cbPoints + cbAbTypes4 - sizeof(U_POINTL) - 1; record = malloc(irecsize); @@ -4244,7 +4266,7 @@ char *U_EMRPOLYDRAW_set( char *U_EMRSETARCDIRECTION_set( const uint32_t iArcDirection ){ - return(U_EMR_CORE3(U_EMR_SETARCDIRECTION, iArcDirection)); + return(U_EMR_CORE3_set(U_EMR_SETARCDIRECTION, iArcDirection)); } // U_EMRSETMITERLIMIT_set 58 @@ -4256,7 +4278,7 @@ char *U_EMRSETARCDIRECTION_set( char *U_EMRSETMITERLIMIT_set( const uint32_t eMiterLimit ){ - return(U_EMR_CORE3(U_EMR_SETMITERLIMIT, eMiterLimit)); + return(U_EMR_CORE3_set(U_EMR_SETMITERLIMIT, eMiterLimit)); } @@ -4266,7 +4288,7 @@ char *U_EMRSETMITERLIMIT_set( \return pointer to U_EMR_BEGINPATH record, or NULL on error. */ char *U_EMRBEGINPATH_set(void){ - return(U_EMR_CORE5(U_EMR_BEGINPATH)); + return(U_EMR_CORE5_set(U_EMR_BEGINPATH)); } // U_EMRENDPATH_set 60 @@ -4275,7 +4297,7 @@ char *U_EMRBEGINPATH_set(void){ \return pointer to U_EMR_ENDPATH record, or NULL on error. */ char *U_EMRENDPATH_set(void){ - return(U_EMR_CORE5(U_EMR_ENDPATH)); + return(U_EMR_CORE5_set(U_EMR_ENDPATH)); } // U_EMRCLOSEFIGURE_set 61 @@ -4284,7 +4306,7 @@ char *U_EMRENDPATH_set(void){ \return pointer to U_EMR_CLOSEFIGURE record, or NULL on error. */ char *U_EMRCLOSEFIGURE_set(void){ - return(U_EMR_CORE5(U_EMR_CLOSEFIGURE)); + return(U_EMR_CORE5_set(U_EMR_CLOSEFIGURE)); } // U_EMRFILLPATH_set 62 @@ -4298,7 +4320,7 @@ char *U_EMRCLOSEFIGURE_set(void){ char *U_EMRFILLPATH_set( const U_RECTL rclBox ){ - return(U_EMR_CORE4(U_EMR_FILLPATH,rclBox)); + return(U_EMR_CORE4_set(U_EMR_FILLPATH,rclBox)); } // U_EMRSTROKEANDFILLPATH_set 63 @@ -4314,7 +4336,7 @@ char *U_EMRFILLPATH_set( char *U_EMRSTROKEANDFILLPATH_set( const U_RECTL rclBox ){ - return(U_EMR_CORE4(U_EMR_STROKEANDFILLPATH,rclBox)); + return(U_EMR_CORE4_set(U_EMR_STROKEANDFILLPATH,rclBox)); } // U_EMRSTROKEPATH_set 64 @@ -4328,7 +4350,7 @@ char *U_EMRSTROKEANDFILLPATH_set( char *U_EMRSTROKEPATH_set( const U_RECTL rclBox ){ - return(U_EMR_CORE4(U_EMR_STROKEPATH,rclBox)); + return(U_EMR_CORE4_set(U_EMR_STROKEPATH,rclBox)); } // U_EMRFLATTENPATH_set 65 @@ -4337,7 +4359,7 @@ char *U_EMRSTROKEPATH_set( \return pointer to U_EMR_FLATTENPATH record, or NULL on error. */ char *U_EMRFLATTENPATH_set(void){ - return(U_EMR_CORE5(U_EMR_FLATTENPATH)); + return(U_EMR_CORE5_set(U_EMR_FLATTENPATH)); } // U_EMRWIDENPATH_set 66 @@ -4346,7 +4368,7 @@ char *U_EMRFLATTENPATH_set(void){ \return pointer to U_EMR_WIDENPATH record, or NULL on error. */ char *U_EMRWIDENPATH_set(void){ - return(U_EMR_CORE5(U_EMR_WIDENPATH)); + return(U_EMR_CORE5_set(U_EMR_WIDENPATH)); } // U_EMRSELECTCLIPPATH_set 67 @@ -4358,7 +4380,7 @@ char *U_EMRWIDENPATH_set(void){ char *U_EMRSELECTCLIPPATH_set( const uint32_t iMode ){ - return(U_EMR_CORE3(U_EMR_SELECTCLIPPATH, iMode)); + return(U_EMR_CORE3_set(U_EMR_SELECTCLIPPATH, iMode)); } // U_EMRABORTPATH_set 68 @@ -4367,7 +4389,7 @@ char *U_EMRSELECTCLIPPATH_set( \return pointer to U_EMR_ABORTPATH record, or NULL on error. */ char *U_EMRABORTPATH_set(void){ - return(U_EMR_CORE5(U_EMR_ABORTPATH)); + return(U_EMR_CORE5_set(U_EMR_ABORTPATH)); } // U_EMRUNDEF69 69 @@ -4416,23 +4438,25 @@ char *U_EMRFILLRGN_set( ){ char *record; int irecsize; - int cbRgns,cbRgns4,off; + int cbRgns,cbRgns4,rds,rds4,off; if(!RgnData)return(NULL); cbRgns = ((PU_RGNDATAHEADER) RgnData)->nRgnSize; - cbRgns4 = UP4(cbRgns); - irecsize = sizeof(U_EMRFILLRGN) + cbRgns4 - sizeof(U_RGNDATAHEADER); // core + array - overlap + cbRgns4 = UP4(cbRgns); + rds = sizeof(U_RGNDATAHEADER) + cbRgns; + rds4 = UP4(rds); + irecsize = sizeof(U_EMRFILLRGN) - sizeof(U_RECTL) + cbRgns4; // core + array - overlap of one rectL record = malloc(irecsize); if(record){ ((PU_EMR) record)->iType = U_EMR_FILLRGN; ((PU_EMR) record)->nSize = irecsize; - ((PU_EMRFILLRGN) record)->rclBounds = rclBounds; // or ??? ((PU_RGNDATAHEADER) RgnData)->rclBounds; - ((PU_EMRFILLRGN) record)->cbRgnData = cbRgns; + ((PU_EMRFILLRGN) record)->rclBounds = rclBounds; + ((PU_EMRFILLRGN) record)->cbRgnData = rds; ((PU_EMRFILLRGN) record)->ihBrush = ihBrush; - off = irecsize - cbRgns4; - memcpy(record + off, RgnData, cbRgns); - off += cbRgns; - if(cbRgns4 > cbRgns){ memset(record + off,0,cbRgns4-cbRgns); } // clear any unused bytes + off = sizeof(U_EMRFILLRGN) - sizeof(U_RGNDATA); + memcpy(record + off, RgnData, rds); + off += rds; + if(rds < rds4){ memset(record + off,0, rds4 - rds); } // clear any unused bytes } return(record); } @@ -4455,24 +4479,26 @@ char *U_EMRFRAMERGN_set( ){ char *record; int irecsize; - int cbRgns,cbRgns4,off; + int cbRgns,cbRgns4,rds,rds4,off; if(!RgnData)return(NULL); cbRgns = ((PU_RGNDATAHEADER) RgnData)->nRgnSize; - cbRgns4 = UP4(cbRgns); - irecsize = sizeof(U_EMRFRAMERGN) + cbRgns4 - sizeof(U_RGNDATAHEADER); // core + array - overlap + cbRgns4 = UP4(cbRgns); + rds = sizeof(U_RGNDATAHEADER) + cbRgns; + rds4 = UP4(rds); + irecsize = sizeof(U_EMRFRAMERGN) - sizeof(U_RECTL) + cbRgns4; // core + array - overlap of one rectL record = malloc(irecsize); if(record){ ((PU_EMR) record)->iType = U_EMR_FRAMERGN; ((PU_EMR) record)->nSize = irecsize; - ((PU_EMRFRAMERGN) record)->rclBounds = rclBounds; // or ??? ((PU_RGNDATAHEADER) RgnData)->rclBounds; - ((PU_EMRFRAMERGN) record)->cbRgnData = cbRgns; + ((PU_EMRFRAMERGN) record)->rclBounds = rclBounds; + ((PU_EMRFRAMERGN) record)->cbRgnData = rds; ((PU_EMRFRAMERGN) record)->ihBrush = ihBrush; ((PU_EMRFRAMERGN) record)->szlStroke = szlStroke; - off = irecsize - cbRgns4; - memcpy(record + off, RgnData, cbRgns); - off += cbRgns; - if(cbRgns4 > cbRgns){ memset(record + off,0,cbRgns4-cbRgns); } // clear any unused bytes + off = sizeof(U_EMRFRAMERGN) - sizeof(U_RGNDATA); + memcpy(record + off, RgnData, rds); + off += rds; + if(rds < rds4){ memset(record + off,0, rds4 - rds); } // clear any unused bytes } return(record); } @@ -4486,7 +4512,7 @@ char *U_EMRFRAMERGN_set( char *U_EMRINVERTRGN_set( const PU_RGNDATA RgnData ){ - return(U_EMR_CORE11(U_EMR_INVERTRGN, RgnData)); + return(U_EMR_CORE11_set(U_EMR_INVERTRGN, RgnData)); } // U_EMRPAINTRGN_set 74 @@ -4498,7 +4524,7 @@ char *U_EMRINVERTRGN_set( char *U_EMRPAINTRGN_set( const PU_RGNDATA RgnData ){ - return(U_EMR_CORE11(U_EMR_PAINTRGN, RgnData)); + return(U_EMR_CORE11_set(U_EMR_PAINTRGN, RgnData)); } // U_EMREXTSELECTCLIPRGN_set 75 @@ -4514,22 +4540,24 @@ char *U_EMREXTSELECTCLIPRGN_set( ){ char *record; int irecsize; - int cbRgns,cbRgns4,off; + int cbRgns,cbRgns4,rds,rds4,off; if(!RgnData)return(NULL); cbRgns = ((PU_RGNDATAHEADER) RgnData)->nRgnSize; - cbRgns4 = UP4(cbRgns); - irecsize = sizeof(U_EMREXTSELECTCLIPRGN) + cbRgns4 - sizeof(U_RGNDATAHEADER); // core + array - overlap + cbRgns4 = UP4(cbRgns); + rds = sizeof(U_RGNDATAHEADER) + cbRgns; + rds4 = UP4(rds); + irecsize = sizeof(U_EMREXTSELECTCLIPRGN) - sizeof(U_RECTL) + cbRgns4; // core + array - overlap of one rectL record = malloc(irecsize); if(record){ ((PU_EMR) record)->iType = U_EMR_EXTSELECTCLIPRGN; ((PU_EMR) record)->nSize = irecsize; - ((PU_EMREXTSELECTCLIPRGN) record)->cbRgnData = cbRgns; + ((PU_EMREXTSELECTCLIPRGN) record)->cbRgnData = rds; ((PU_EMREXTSELECTCLIPRGN) record)->iMode = iMode; - off = irecsize - cbRgns4; - memcpy(record + off, RgnData, cbRgns); - off += cbRgns; - if(cbRgns4 > cbRgns){ memset(record + off,0,cbRgns4-cbRgns); } // clear any unused bytes + off = sizeof(U_EMREXTSELECTCLIPRGN) - sizeof(U_RGNDATA); + memcpy(record + off, RgnData, rds); + off += rds; + if(rds < rds4){ memset(record + off,0, rds4 - rds); } // clear any unused bytes } return(record); } @@ -4568,7 +4596,6 @@ char *U_EMRBITBLT_set( int cbImage,cbImage4,cbBmi,off; SET_CB_FROM_PXBMI(Px,Bmi,cbImage,cbImage4,cbBmi,cbPx); - irecsize = sizeof(U_EMRBITBLT) + cbBmi + cbImage4; record = malloc(irecsize); if(record){ @@ -4933,7 +4960,7 @@ char *U_EMREXTTEXTOUTA_set( const U_FLOAT eyScale, const PU_EMRTEXT emrtext ){ - return(U_EMR_CORE8(U_EMR_EXTTEXTOUTA,rclBounds, iGraphicsMode, exScale, eyScale,emrtext)); + return(U_EMR_CORE8_set(U_EMR_EXTTEXTOUTA,rclBounds, iGraphicsMode, exScale, eyScale,emrtext)); } // U_EMREXTTEXTOUTW_set 84 @@ -4953,7 +4980,7 @@ char *U_EMREXTTEXTOUTW_set( const U_FLOAT eyScale, const PU_EMRTEXT emrtext ){ - return(U_EMR_CORE8(U_EMR_EXTTEXTOUTW,rclBounds, iGraphicsMode, exScale, eyScale,emrtext)); + return(U_EMR_CORE8_set(U_EMR_EXTTEXTOUTW,rclBounds, iGraphicsMode, exScale, eyScale,emrtext)); } // U_EMRPOLYBEZIER16_set 85 @@ -4969,7 +4996,7 @@ char *U_EMRPOLYBEZIER16_set( const uint32_t cpts, const U_POINT16 *points ){ - return(U_EMR_CORE6(U_EMR_POLYBEZIER16, rclBounds, cpts, points)); + return(U_EMR_CORE6_set(U_EMR_POLYBEZIER16, rclBounds, cpts, points)); } // U_EMRPOLYGON16_set 86 @@ -4985,7 +5012,7 @@ char *U_EMRPOLYGON16_set( const uint32_t cpts, const U_POINT16 *points ){ - return(U_EMR_CORE6(U_EMR_POLYGON16, rclBounds, cpts, points)); + return(U_EMR_CORE6_set(U_EMR_POLYGON16, rclBounds, cpts, points)); } // U_EMRPOLYLINE16_set 87 @@ -5001,7 +5028,7 @@ char *U_EMRPOLYLINE16_set( const uint32_t cpts, const U_POINT16 *points ){ - return(U_EMR_CORE6(U_EMR_POLYLINE16, rclBounds, cpts, points)); + return(U_EMR_CORE6_set(U_EMR_POLYLINE16, rclBounds, cpts, points)); } // U_EMRPOLYBEZIERTO16_set 88 @@ -5017,7 +5044,7 @@ char *U_EMRPOLYBEZIERTO16_set( const uint32_t cpts, const U_POINT16 *points ){ - return(U_EMR_CORE6(U_EMR_POLYBEZIERTO16, rclBounds, cpts, points)); + return(U_EMR_CORE6_set(U_EMR_POLYBEZIERTO16, rclBounds, cpts, points)); } // U_EMRPOLYLINETO16_set 89 @@ -5033,7 +5060,7 @@ char *U_EMRPOLYLINETO16_set( const uint32_t cpts, const U_POINT16 *points ){ - return(U_EMR_CORE6(U_EMR_POLYLINETO16, rclBounds, cpts, points)); + return(U_EMR_CORE6_set(U_EMR_POLYLINETO16, rclBounds, cpts, points)); } // U_EMRPOLYPOLYLINE16_set 90 @@ -5053,7 +5080,7 @@ char *U_EMRPOLYPOLYLINE16_set( const uint32_t cpts, const U_POINT16 *points ){ - return(U_EMR_CORE10(U_EMR_POLYPOLYLINE16, rclBounds, nPolys, aPolyCounts,cpts, points)); + return(U_EMR_CORE10_set(U_EMR_POLYPOLYLINE16, rclBounds, nPolys, aPolyCounts,cpts, points)); } // U_EMRPOLYPOLYGON16_set 91 @@ -5073,7 +5100,7 @@ char *U_EMRPOLYPOLYGON16_set( const uint32_t cpts, const U_POINT16 *points ){ - return(U_EMR_CORE10(U_EMR_POLYPOLYGON16, rclBounds, nPolys, aPolyCounts,cpts, points)); + return(U_EMR_CORE10_set(U_EMR_POLYPOLYGON16, rclBounds, nPolys, aPolyCounts,cpts, points)); } @@ -5098,7 +5125,7 @@ char *U_EMRPOLYDRAW16_set( if(!cpts || !aptl || !abTypes)return(NULL); cbPoints = cpts * sizeof(U_POINT16); // space for aptl - cbAbTypes = cpts; // number of abTypes (also size, 1 byte each) + cbAbTypes = cpts; // number of abTypes (same array size, 1 byte each) cbAbTypes4 = UP4(cbAbTypes); // space for abTypes irecsize = sizeof(U_EMRPOLYDRAW16) + cbPoints + cbAbTypes4 - sizeof(U_POINT16) - 1; record = malloc(irecsize); @@ -5242,7 +5269,7 @@ char *U_EMREXTCREATEPEN_set( char *U_EMRSETICMMODE_set( const uint32_t iMode ){ - return(U_EMR_CORE3(U_EMR_SETICMMODE, iMode)); + return(U_EMR_CORE3_set(U_EMR_SETICMMODE, iMode)); } // U_EMRCREATECOLORSPACE_set 99 @@ -5280,7 +5307,7 @@ char *U_EMRCREATECOLORSPACE_set( char *U_EMRSETCOLORSPACE_set( const uint32_t ihCS ){ - return(U_EMR_CORE3(U_EMR_SETCOLORSPACE, ihCS)); + return(U_EMR_CORE3_set(U_EMR_SETCOLORSPACE, ihCS)); } // U_EMRDELETECOLORSPACE_set 101 @@ -5292,7 +5319,7 @@ char *U_EMRSETCOLORSPACE_set( char *U_EMRDELETECOLORSPACE_set( const uint32_t ihCS ){ - return(U_EMR_CORE3(U_EMR_DELETECOLORSPACE, ihCS)); + return(U_EMR_CORE3_set(U_EMR_DELETECOLORSPACE, ihCS)); } // U_EMRGLSRECORD_set 102 Not implemented @@ -5428,7 +5455,7 @@ char *U_EMRALPHABLEND_set( \param iMode Mirroring Enumeration */ char *U_EMRSETLAYOUT_set(uint32_t iMode){ - return(U_EMR_CORE3(U_EMR_SETLAYOUT, iMode)); + return(U_EMR_CORE3_set(U_EMR_SETLAYOUT, iMode)); } // U_EMRTRANSPARENTBLT_set 116 diff --git a/src/libuemf/uemf.h b/src/libuemf/uemf.h index a792a65de..f6ed7da03 100644 --- a/src/libuemf/uemf.h +++ b/src/libuemf/uemf.h @@ -95,11 +95,11 @@ these WMF enumerations is by referencing the following table: /* File: uemf.h -Version: 0.0.27 -Date: 28-MAR-2014 +Version: 0.0.32 +Date: 28-APR-2015 Author: David Mathog, Biology Division, Caltech email: mathog@caltech.edu -Copyright: 2014 David Mathog and California Institute of Technology (Caltech) +Copyright: 2015 David Mathog and California Institute of Technology (Caltech) */ #ifndef _UEMF_ @@ -167,6 +167,14 @@ extern "C" { /** Solaris 8 has problems with round/roundf, just use this everywhere */ #define U_ROUND(A) ( (A) > 0 ? floor((A)+0.5) : ( (A) < 0 ? -floor(-(A)+0.5) : (A) ) ) +#define MAKE_MIN_PTR(A,B) ( A < B ? A : B) +/* This is tricky. The next one can be called with a size which is either an int or an unsigned int. + The former can be negative, which is obviously wrong, but testing for that means that the size cannot + be more than INT_MAX/2. Accept that limitation since no reasonable EMF record or file should ever be that large. + B must be an INT or size_t. + If a uint16_t is used gcc complains about the first test. Force B to be at least as big as int (at run time) */ +#define IS_MEM_UNSAFE(A,B,C) ( (sizeof(B) < sizeof(int) || (int)(B)) < 0 ? 1 : ((int8_t *)(A) > (int8_t *)(C) ? 1 : ((int8_t *)(C) - (int8_t *)(A) >= (int)(B) ? 0 : 1 ))) //!< Return 1 when a region of memory starting at A of B bytes extends beyond pointer C + /** @} */ @@ -1799,7 +1807,7 @@ typedef struct { //!< In MS documentation this is Log *PU_PATTERN; //!< EMF manual 2.2.12 /** - \brief For U_LOGFONT_PANOSE elfLogFont field + \brief For U_LOGFONT elfLogFont field EMF manual 2.2.13, Microsoft name: LogFont Object */ @@ -2075,6 +2083,9 @@ typedef struct { // *********************************************************************************** // The following have U_EMR_# records. They are ordered by their record index, not by EMF manual position. +// records which are documented but not implemented +#define U_SIZE_EMRNOTIMPLEMENTED 2048 + /* Index 1 */ /** \brief The first U_ENHMETARECORD record in the metafile. @@ -2117,6 +2128,8 @@ typedef struct { U_SIZEL szlMicrometers; //!< Size of the display device in micrometer } U_EMRHEADER, *PU_EMRHEADER; //!< EMF manual 2.2.9 +#define U_SIZE_EMRHEADER sizeof(U_EMRHEADER) // modern EMF files, for _really_ old ones the _MIN size applies +#define U_SIZE_EMRHEADER_MIN (U_SIZE_EMRHEADER - sizeof(U_CBPXLFMT) - sizeof(U_OFFPXLFMT) - sizeof(uint32_t) - sizeof(U_SIZEL)) /* Index 2,3,4,5,6 */ /** EMF manual 2.3.5.16 @@ -2137,6 +2150,11 @@ typedef struct { *PU_EMRPOLYBEZIERTO, //!< EMF manual 2.3.5.18 *PU_EMRPOLYLINETO; //!< EMF manual 2.3.5.26 +#define U_SIZE_EMRPOLYBEZIER (sizeof(U_EMR) + sizeof(U_RECTL) + sizeof(U_NUM_POINTL) ) +#define U_SIZE_EMRPOLYGON U_SIZE_EMRPOLYBEZIER +#define U_SIZE_EMRPOLYLINE U_SIZE_EMRPOLYBEZIER +#define U_SIZE_EMRPOLYBEZIERTO U_SIZE_EMRPOLYBEZIER +#define U_SIZE_EMRPOLYLINETO U_SIZE_EMRPOLYBEZIER /* Index 7,8 */ /** EMF manual 2.3.5.30 @@ -2153,6 +2171,8 @@ typedef struct { U_EMRPOLYPOLYGON, //!< EMF manual 2.3.5.28 *PU_EMRPOLYPOLYLINE, //!< EMF manual 2.3.5.30 *PU_EMRPOLYPOLYGON; //!< EMF manual 2.3.5.28 +#define U_SIZE_EMRPOLYPOLYLINE (sizeof(U_EMR) + sizeof(U_RECTL) + sizeof(U_NUM_POLYCOUNTS) + sizeof(U_POLYCOUNTS)) +#define U_SIZE_EMRPOLYPOLYGON U_SIZE_EMRPOLYPOLYLINE /* Index 9,11 (numbers interleave with next one) */ /** EMF manual 2.3.11.30 @@ -2164,6 +2184,8 @@ typedef struct { U_EMRSETVIEWPORTEXTEX, //!< EMF manual manual 2.3.11.28 *PU_EMRSETWINDOWEXTEX, //!< EMF manual manual 2.3.11.30 *PU_EMRSETVIEWPORTEXTEX; //!< EMF manual manual 2.3.11.28 +#define U_SIZE_EMRSETWINDOWEXTEX (sizeof(U_EMRSETWINDOWEXTEX )) +#define U_SIZE_EMRSETVIEWPORTEXTEX (sizeof(U_EMRSETVIEWPORTEXTEX)) /* Index 10,12,13 */ /** EMF manual 2.3.11.31 @@ -2177,7 +2199,9 @@ typedef struct { *PU_EMRSETWINDOWORGEX, //!< EMF manual 2.3.11.31 *PU_EMRSETVIEWPORTORGEX, //!< EMF manual 2.3.11.29 *PU_EMRSETBRUSHORGEX; //!< EMF manual 2.3.11.12 - +#define U_SIZE_EMRSETWINDOWORGEX (sizeof(U_EMRSETWINDOWORGEX )) +#define U_SIZE_EMRSETVIEWPORTORGEX (sizeof(U_EMRSETVIEWPORTORGEX)) +#define U_SIZE_EMRSETBRUSHORGEX (sizeof(U_EMRSETBRUSHORGEX )) /* Index 14 */ /** EMF manual 3.2.4.1 @@ -2197,6 +2221,7 @@ typedef struct { U_OFFPLENTRIES offPalEntries; //!< Offset in bytes to array of palette entries } U_EMREOF, *PU_EMREOF; //!< EMF manual 3.2.4.1 +#define U_SIZE_EMREOF (sizeof(U_EMREOF)) /* Index 15 */ /** EMF manual 2.3.5.36 @@ -2207,6 +2232,7 @@ typedef struct { U_COLORREF crColor; //!< Pixel color } U_EMRSETPIXELV, *PU_EMRSETPIXELV; //!< EMF manual 2.3.5.36 +#define U_SIZE_EMRSETPIXELV (sizeof(U_EMRSETPIXELV)) /* Index 16 */ /** EMF manual 2.3.11.20 @@ -2216,6 +2242,7 @@ typedef struct { uint32_t dwFlags; //!< must be 1 } U_EMRSETMAPPERFLAGS, *PU_EMRSETMAPPERFLAGS; //!< EMF manual 2.3.11.20 +#define U_SIZE_EMRSETMAPPERFLAGS (sizeof(U_EMRSETMAPPERFLAGS)) /* Index 17,18,19,20,21,22,67,98,115 */ @@ -2243,6 +2270,16 @@ typedef struct { *PU_EMRSETICMMODE, //!< EMF manual 2.3.11.14 ICMMode Enumeration *PU_EMRSETLAYOUT; //!< EMF manual 2.3.11.17 Mirroring Enumeration +#define U_SIZE_EMRSETMAPMODE (sizeof(U_EMRSETMAPMODE )) +#define U_SIZE_EMRSETBKMODE (sizeof(U_EMRSETBKMODE )) +#define U_SIZE_EMRSETPOLYFILLMODE (sizeof(U_EMRSETPOLYFILLMODE )) +#define U_SIZE_EMRSETROP2 (sizeof(U_EMRSETROP2 )) +#define U_SIZE_EMRSETSTRETCHBLTMODE (sizeof(U_EMRSETSTRETCHBLTMODE)) +#define U_SIZE_EMRSETTEXTALIGN (sizeof(U_EMRSETTEXTALIGN )) +#define U_SIZE_EMRSELECTCLIPPATH (sizeof(U_EMRSELECTCLIPPATH )) +#define U_SIZE_EMRSETICMMODE (sizeof(U_EMRSETICMMODE )) +#define U_SIZE_EMRSETLAYOUT (sizeof(U_EMRSETLAYOUT )) + /* Index 23 */ /** EMF manual 2.3.11.13 */ @@ -2251,6 +2288,7 @@ typedef struct { U_COLORADJUSTMENT ColorAdjustment; //!< Color Adjustment } U_EMRSETCOLORADJUSTMENT, *PU_EMRSETCOLORADJUSTMENT; //!< EMF manual 2.3.11.13 +#define U_SIZE_EMRSETCOLORADJUSTMENT (sizeof(U_EMRSETCOLORADJUSTMENT)) /* Index 24, 25 */ /** EMF manual 2.3.11.26 @@ -2262,6 +2300,8 @@ typedef struct { U_EMRSETBKCOLOR, //!< EMF manual 2.3.11.10 *PU_EMRSETTEXTCOLOR, //!< EMF manual 2.3.11.26 *PU_EMRSETBKCOLOR; //!< EMF manual 2.3.11.10 +#define U_SIZE_EMRSETTEXTCOLOR (sizeof(U_EMRSETTEXTCOLOR)) +#define U_SIZE_EMRSETBKCOLOR (sizeof(U_EMRSETBKCOLOR )) /* Index 26 */ /** EMF manual 2.3.2.4 @@ -2271,6 +2311,7 @@ typedef struct { U_POINTL ptlOffset; //!< Clipping region } U_EMROFFSETCLIPRGN, *PU_EMROFFSETCLIPRGN; //!< EMF manual 2.3.2.4 +#define U_SIZE_EMROFFSETCLIPRGN (sizeof(U_EMROFFSETCLIPRGN)) /* Index 27, 54 */ /** @@ -2284,6 +2325,8 @@ typedef struct { U_EMRLINETO, //!< EMF manual 2.3.5.13 *PU_EMRMOVETOEX, //!< EMF manual 2.3.11.4 *PU_EMRLINETO; //!< EMF manual 2.3.5.13 +#define U_SIZE_EMRMOVETOEX (sizeof(U_EMRMOVETOEX)) +#define U_SIZE_EMRLINETO (sizeof(U_EMRLINETO )) /* Index 28,33,52,59,60,61,65,66,68 */ /** EMF manual 2.3.2 @@ -2309,6 +2352,15 @@ typedef struct { *PU_EMRFLATTENPATH, //!< EMF manual 2.3.10 *PU_EMRWIDENPATH, //!< EMF manual 2.3.10 *PU_EMRABORTPATH; //!< EMF manual 2.3.10 +#define U_SIZE_EMRSETMETARGN (sizeof(U_EMRSETMETARGN )) +#define U_SIZE_EMRSAVEDC (sizeof(U_EMRSAVEDC )) +#define U_SIZE_EMRREALIZEPALETTE (sizeof(U_EMRREALIZEPALETTE)) +#define U_SIZE_EMRBEGINPATH (sizeof(U_EMRBEGINPATH )) +#define U_SIZE_EMRENDPATH (sizeof(U_EMRENDPATH )) +#define U_SIZE_EMRCLOSEFIGURE (sizeof(U_EMRCLOSEFIGURE )) +#define U_SIZE_EMRFLATTENPATH (sizeof(U_EMRFLATTENPATH )) +#define U_SIZE_EMRWIDENPATH (sizeof(U_EMRWIDENPATH )) +#define U_SIZE_EMRABORTPATH (sizeof(U_EMRABORTPATH )) /* Index 29,30 */ /** EMF manual 2.3.2.1 @@ -2320,6 +2372,8 @@ typedef struct { U_EMRINTERSECTCLIPRECT, //!< EMF manual 2.3.2.3 *PU_EMREXCLUDECLIPRECT, //!< EMF manual 2.3.2.1 *PU_EMRINTERSECTCLIPRECT; //!< EMF manual 2.3.2.3 +#define U_SIZE_EMREXCLUDECLIPRECT (sizeof(U_EMREXCLUDECLIPRECT )) +#define U_SIZE_EMRINTERSECTCLIPRECT (sizeof(U_EMRINTERSECTCLIPRECT )) /* Index 31,32 */ /** EMF manual 2.3.11.7 @@ -2334,6 +2388,8 @@ typedef struct { U_EMRSCALEWINDOWEXTEX, //!< EMF manual 2.3.11.8 *PU_EMRSCALEVIEWPORTEXTEX, //!< EMF manual 2.3.11.7 *PU_EMRSCALEWINDOWEXTEX; //!< EMF manual 2.3.11.8 +#define U_SIZE_EMRSCALEVIEWPORTEXTEX (sizeof(U_EMRSCALEVIEWPORTEXTEX)) +#define U_SIZE_EMRSCALEWINDOWEXTEX (sizeof(U_EMRSCALEWINDOWEXTEX )) /* Index 33 (see 28) */ @@ -2345,6 +2401,7 @@ typedef struct { int32_t iRelative; //!< DC to restore. -1 is preceding } U_EMRRESTOREDC, *PU_EMRRESTOREDC; //!< EMF manual 2.3.11.6 +#define U_SIZE_EMRRESTOREDC (sizeof(U_EMRRESTOREDC)) /* Index 35 */ /** EMF manual 2.3.12.2 @@ -2354,6 +2411,7 @@ typedef struct { U_XFORM xform; //!< Transform } U_EMRSETWORLDTRANSFORM, *PU_EMRSETWORLDTRANSFORM; //!< EMF manual 2.3.12.2 +#define U_SIZE_EMRSETWORLDTRANSFORM (sizeof(U_EMRSETWORLDTRANSFORM)) /* Index 36 */ /** EMF manual 2.3.12.1 @@ -2364,6 +2422,7 @@ typedef struct { uint32_t iMode; //!< ModifyWorldTransformMode Enumeration } U_EMRMODIFYWORLDTRANSFORM, *PU_EMRMODIFYWORLDTRANSFORM; //!< EMF manual 2.3.12.1 +#define U_SIZE_EMRMODIFYWORLDTRANSFORM (sizeof(U_EMRMODIFYWORLDTRANSFORM)) /* Index 37,40 */ /** EMF manual 2.3.8.3 @@ -2375,6 +2434,8 @@ typedef struct { U_EMRSELECTOBJECT, //!< EMF manual 2.3.8.5 *PU_EMRDELETEOBJECT, //!< EMF manual 2.3.8.3 *PU_EMRSELECTOBJECT; //!< EMF manual 2.3.8.5 +#define U_SIZE_EMRDELETEOBJECT (sizeof(U_EMRDELETEOBJECT)) +#define U_SIZE_EMRSELECTOBJECT (sizeof(U_EMRSELECTOBJECT)) /* Index 38 */ /** EMF manual 2.3.7.7 @@ -2385,6 +2446,7 @@ typedef struct { U_LOGPEN lopn; //!< Pen properties } U_EMRCREATEPEN, *PU_EMRCREATEPEN; //!< EMF manual 2.3.7.7 +#define U_SIZE_EMRCREATEPEN (sizeof(U_EMRCREATEPEN)) /* Index 39 */ /** EMF manual 2.3.7.1 @@ -2395,6 +2457,7 @@ typedef struct { U_LOGBRUSH lb; //!< Brush properties } U_EMRCREATEBRUSHINDIRECT, *PU_EMRCREATEBRUSHINDIRECT; //!< EMF manual 2.3.7.1 +#define U_SIZE_EMRCREATEBRUSHINDIRECT (sizeof(U_EMRCREATEBRUSHINDIRECT)) /* Index 40 see 37 */ @@ -2409,6 +2472,7 @@ typedef struct { U_FLOAT eSweepAngle; //!< Sweep angle in degrees } U_EMRANGLEARC, *PU_EMRANGLEARC; //!< EMF manual 2.3.5.1 +#define U_SIZE_EMRANGLEARC (sizeof(U_EMRANGLEARC)) /* Index 42,43 */ /** EMF manual 2.3.5.5 @@ -2420,6 +2484,8 @@ typedef struct { U_EMRRECTANGLE, //!< EMF manual 2.3.5.5 *PU_EMRELLIPSE, //!< EMF manual 2.3.5.5 *PU_EMRRECTANGLE; //!< EMF manual 2.3.5.34 +#define U_SIZE_EMRELLIPSE (sizeof(U_EMRELLIPSE )) +#define U_SIZE_EMRRECTANGLE (sizeof(U_EMRRECTANGLE)) /* Index 44 */ /** EMF manual 2.3.5.35 @@ -2430,6 +2496,7 @@ typedef struct { U_SIZEL szlCorner; //!< W & H in logical units of ellipse used to round corner } U_EMRROUNDRECT, *PU_EMRROUNDRECT; //!< EMF manual 2.3.5.35 +#define U_SIZE_EMRROUNDRECT (sizeof(U_EMRROUNDRECT)) /* Index 45, 46 ,47, 55 */ /** EMF manual 2.3.5.2 @@ -2447,6 +2514,10 @@ typedef struct { *PU_EMRCHORD, //!< EMF manual 2.3.5.4 *PU_EMRPIE, //!< EMF manual 2.3.5.15 *PU_EMRARCTO; //!< EMF manual 2.3.5.3 +#define U_SIZE_EMRARC (sizeof(U_EMRARC )) +#define U_SIZE_EMRCHORD (sizeof(U_EMRCHORD)) +#define U_SIZE_EMRPIE (sizeof(U_EMRPIE )) +#define U_SIZE_EMRARCTO (sizeof(U_EMRARCTO)) /* Index 48 */ /** EMF manual 2.3.8.6 @@ -2456,6 +2527,7 @@ typedef struct { uint32_t ihPal; //!< Index of a Palette object in the EMF object table } U_EMRSELECTPALETTE, *PU_EMRSELECTPALETTE; //!< EMF manual 2.3.8.6 +#define U_SIZE_EMRSELECTPALETTE (sizeof(U_EMRSELECTPALETTE)) /* Index 49 */ /** EMF manual 2.3.7.6 @@ -2466,6 +2538,7 @@ typedef struct { U_LOGPALETTE lgpl; //!< Palette properties } U_EMRCREATEPALETTE, *PU_EMRCREATEPALETTE; //!< EMF manual 2.3.7.6 +#define U_SIZE_EMRCREATEPALETTE (sizeof(U_EMRCREATEPALETTE)) /* Index 50 */ /** EMF manual 2.3.8.8 @@ -2478,6 +2551,7 @@ typedef struct { U_LOGPLTNTRY aPalEntries[1]; //!< Values to set with } U_EMRSETPALETTEENTRIES, *PU_EMRSETPALETTEENTRIES; //!< EMF manual 2.3.8.8 +#define U_SIZE_EMRSETPALETTEENTRIES (sizeof(U_EMR) + 2*sizeof(uint32_t) + sizeof(U_NUM_LOGPLTNTRY)) /* Index 51 */ /** EMF manual 2.3.8.4 @@ -2488,6 +2562,7 @@ typedef struct { uint32_t cEntries; //!< Number to expand or truncate the Palette entry list to. } U_EMRRESIZEPALETTE, *PU_EMRRESIZEPALETTE; //!< EMF manual 2.3.8.4 +#define U_SIZE_EMRRESIZEPALETTE (sizeof(U_EMRRESIZEPALETTE)) /* Index 52 (see 28) */ @@ -2501,22 +2576,26 @@ typedef struct { uint32_t iMode; //!< FloodFill Enumeration } U_EMREXTFLOODFILL, *PU_EMREXTFLOODFILL; //!< EMF manual 2.3.5.6 +#define U_SIZE_EMREXTFLOODFILL (sizeof(U_EMREXTFLOODFILL)) /* Index 54 (see 27) */ /* Index 55 (see 45) */ /* Index 56 */ -/** EMF manual 2.3.5.20 +/** EMF manual 2.3.5.20 + + followed by + uint8_t abTypes[1]; //!< Array of Point Enumeration */ typedef struct { U_EMR emr; //!< U_EMR U_RECTL rclBounds; //!< Bounding rectangle in device units U_NUM_POINTL cptl; //!< Number of U_POINTL objects U_POINTL aptl[1]; //!< Array of U_POINTL objects - uint8_t abTypes[1]; //!< Array of Point Enumeration } U_EMRPOLYDRAW, *PU_EMRPOLYDRAW; //!< EMF manual 2.3.5.20 +#define U_SIZE_EMRPOLYDRAW (sizeof(U_EMR) + sizeof(U_RECTL) + sizeof(U_NUM_POINTL)) /* Index 57 */ /** EMF manual 2.3.11.9 @@ -2526,6 +2605,7 @@ typedef struct { uint32_t iArcDirection; //!< ArcDirection Enumeration } U_EMRSETARCDIRECTION, *PU_EMRSETARCDIRECTION; //!< EMF manual 2.3.11.9 +#define U_SIZE_EMRSETARCDIRECTION (sizeof(U_EMRSETARCDIRECTION)) /* Index 58 */ /** EMF manual 2.3.11.21 @@ -2538,6 +2618,7 @@ typedef struct { uint32_t eMiterLimit; //!< Miter limit (max value of mitered length / line width) } U_EMRSETMITERLIMIT, *PU_EMRSETMITERLIMIT; //!< EMF manual 2.3.11.21 +#define U_SIZE_EMRSETMITERLIMIT (sizeof(U_EMRSETMITERLIMIT)) /* Index 59,60,61 (see 28) */ @@ -2553,6 +2634,9 @@ typedef struct { *PU_EMRFILLPATH, //!< EMF manual 2.3.5.9 *PU_EMRSTROKEANDFILLPATH, //!< EMF manual 2.3.5.38 *PU_EMRSTROKEPATH; //!< EMF manual 2.3.5.39 +#define U_SIZE_EMRFILLPATH (sizeof(U_EMRFILLPATH )) +#define U_SIZE_EMRSTROKEANDFILLPATH (sizeof(U_EMRSTROKEANDFILLPATH)) +#define U_SIZE_EMRSTROKEPATH (sizeof(U_EMRSTROKEPATH )) /* Index 65,66 (see 28) */ /* Index 67 (see 17) */ @@ -2569,6 +2653,7 @@ typedef struct { uint8_t Data[1]; //!< Comment (any binary data, interpretation is program specific) } U_EMRCOMMENT, *PU_EMRCOMMENT; //!< EMF manual 2.3.3.1, AKA GDICOMMENT +#define U_SIZE_EMRCOMMENT (sizeof(U_EMR) + sizeof(U_CBDATA)) /* variant comment types */ /** EMF manual 2.3.3.2 @@ -2580,6 +2665,7 @@ typedef struct { uint8_t Data[1]; //!< EMF Plus record } U_EMRCOMMENT_EMFPLUS, *PU_EMRCOMMENT_EMFPLUS; //!< EMF manual 2.3.3.2, EMF Plus comment +#define U_SIZE_EMRCOMMENT_EMFPLUS (sizeof(U_EMR) + sizeof(U_CBDATA) + sizeof(uint32_t)) /** EMF manual 2.3.3.3 */ @@ -2591,6 +2677,7 @@ typedef struct { uint8_t Data[1]; //!< EMF Spool records } U_EMRCOMMENT_SPOOL, *PU_EMRCOMMENT_SPOOL; //!< EMF manual 2.3.3.3, EMF Spool comment +#define U_SIZE_EMRCOMMENT_SPOOL (sizeof(U_EMR) + sizeof(U_CBDATA) + 2*sizeof(uint32_t)) /** EMF manual 2.3.3.4 */ @@ -2602,9 +2689,11 @@ typedef struct { uint8_t Data[1]; //!< Public comment data } U_EMRCOMMENT_PUBLIC, *PU_EMRCOMMENT_PUBLIC; //!< EMF manual 2.3.3.4, EMF Public comment +#define U_SIZE_EMRCOMMENT_PUBLIC (sizeof(U_EMR) + sizeof(U_CBDATA) + 2*sizeof(uint32_t)) /* Index 71 */ -/** EMF manual 2.3.5.10 +/** EMF manual 2.3.5.10 + followed by a variable number of U_RECTLs */ typedef struct { U_EMR emr; //!< U_EMR @@ -2614,6 +2703,7 @@ typedef struct { U_RGNDATA RgnData[1]; //!< Variable size U_RGNDATA structure } U_EMRFILLRGN, *PU_EMRFILLRGN; //!< EMF manual 2.3.5.10 +#define U_SIZE_EMRFILLRGN (sizeof(U_EMR) + sizeof(U_RECTL) + sizeof(U_CBRGNDATA) + sizeof(uint32_t)) /* Index 72 */ /** EMF manual 2.3.5.11 @@ -2627,6 +2717,7 @@ typedef struct { U_RGNDATA RgnData[1]; //!< Variable size U_RGNDATA structure } U_EMRFRAMERGN, *PU_EMRFRAMERGN; //!< EMF manual 2.3.5.11 +#define U_SIZE_EMRFRAMERGN (sizeof(U_EMR) + sizeof(U_RECTL) + sizeof(U_CBRGNDATA) + sizeof(uint32_t) + sizeof(U_SIZEL)) /* Index 73,74 */ /** EMF manual 2.3.11.3 @@ -2640,6 +2731,8 @@ typedef struct { U_EMRPAINTRGN, //!< EMF manual 2.3.5.14 *PU_EMRINVERTRGN, //!< EMF manual 2.3.11.3 *PU_EMRPAINTRGN; //!< EMF manual 2.3.5.14 +#define U_SIZE_EMRINVERTRGN (sizeof(U_EMR) + sizeof(U_RECTL) + sizeof(U_CBRGNDATA)) +#define U_SIZE_EMRPAINTRGN U_SIZE_EMRINVERTRGN /* Index 75 */ /** EMF manual 2.3.2.2 @@ -2651,6 +2744,7 @@ typedef struct { U_RGNDATA RgnData[1]; //!< Variable size U_RGNDATA structure } U_EMREXTSELECTCLIPRGN, *PU_EMREXTSELECTCLIPRGN; //!< EMF manual 2.3.2.2 +#define U_SIZE_EMREXTSELECTCLIPRGN (sizeof(U_EMR) + sizeof(U_CBRGNDATA) + sizeof(uint32_t)) /* Index 76 */ /** EMF manual 2.3.1.2 @@ -2672,6 +2766,7 @@ typedef struct { //!< Record may include optional bitmapbuffer } U_EMRBITBLT, *PU_EMRBITBLT; //!< EMF manual 2.3.1.2 +#define U_SIZE_EMRBITBLT (sizeof(U_EMRBITBLT)) /* Index 77 */ /** EMF manual 2.3.1.6 @@ -2694,6 +2789,7 @@ typedef struct { //!< Record may include optional bitmapbuffer } U_EMRSTRETCHBLT, *PU_EMRSTRETCHBLT; //!< EMF manual 2.3.1.6 +#define U_SIZE_EMRSTRETCHBLT (sizeof(U_EMRSTRETCHBLT)) /* Index 78 */ /** EMF manual 2.3.1.3 @@ -2721,6 +2817,7 @@ typedef struct { //!< Record may include optional Source and mask bitmapbuffers } U_EMRMASKBLT, *PU_EMRMASKBLT; //!< EMF manual 2.3.1.3 +#define U_SIZE_EMRMASKBLT (sizeof(U_EMRMASKBLT)) /* Index 79 */ /** EMF manual 2.3.1.4 @@ -2747,6 +2844,7 @@ typedef struct { //!< Record may include optional Source and mask bitmapbuffers } U_EMRPLGBLT, *PU_EMRPLGBLT; //!< EMF manual 2.3.1.4 +#define U_SIZE_EMRPLGBLT (sizeof(U_EMRPLGBLT)) /* Index 80 */ /** EMF manual 2.3.1.5 @@ -2767,6 +2865,7 @@ typedef struct { //!< Record may includes optional bitmapbuffer } U_EMRSETDIBITSTODEVICE, *PU_EMRSETDIBITSTODEVICE; //!< EMF manual 2.3.1.5 +#define U_SIZE_EMRSETDIBITSTODEVICE (sizeof(U_EMRSETDIBITSTODEVICE)) /* Index 81 */ /** EMF manual 2.3.1.7 @@ -2787,6 +2886,7 @@ typedef struct { //!< Record may includes optional bitmapbuffer } U_EMRSTRETCHDIBITS, *PU_EMRSTRETCHDIBITS; //!< EMF manual 2.3.1.7 +#define U_SIZE_EMRSTRETCHDIBITS (sizeof(U_EMRSTRETCHDIBITS)) /* Index 82 */ /** EMF manual 2.3.7.8 @@ -2797,6 +2897,8 @@ typedef struct { U_LOGFONT_PANOSE elfw; //!< Font parameters, either U_LOGFONT or U_LOGFONT_PANOSE, the latter is bigger so use that type here } U_EMREXTCREATEFONTINDIRECTW, *PU_EMREXTCREATEFONTINDIRECTW; //!< EMF manual 2.3.7.8 +#define U_SIZE_EMREXTCREATEFONTINDIRECTW_LOGFONT_PANOSE (sizeof(U_EMR) + 4 + sizeof(U_LOGFONT_PANOSE)) +#define U_SIZE_EMREXTCREATEFONTINDIRECTW_LOGFONT (sizeof(U_EMR) + 4 + sizeof(U_LOGFONT)) /* Index 83,84 */ /** EMF manual 2.3.5.7 @@ -2820,6 +2922,8 @@ typedef struct { U_EMREXTTEXTOUTW, //!< EMF manual 2.3.5.8 *PU_EMREXTTEXTOUTA, //!< EMF manual 2.3.5.7 *PU_EMREXTTEXTOUTW; //!< EMF manual 2.3.5.8 +#define U_SIZE_EMREXTTEXTOUTA (sizeof(U_EMREXTTEXTOUTA)) +#define U_SIZE_EMREXTTEXTOUTW (sizeof(U_EMREXTTEXTOUTW)) /* Index 85,86,87,88,89 */ /** EMF manual 2.3.5.17 @@ -2839,9 +2943,16 @@ typedef struct { *PU_EMRPOLYLINE16, //!< EMF manual 2.3.5.25 *PU_EMRPOLYBEZIERTO16, //!< EMF manual 2.3.5.19 *PU_EMRPOLYLINETO16; //!< EMF manual 2.3.5.27 - +#define U_SIZE_EMRPOLYBEZIER16 (sizeof(U_EMR) + sizeof(U_RECTL) + sizeof(U_NUM_POINT16)) +#define U_SIZE_EMRPOLYGON16 U_SIZE_EMRPOLYBEZIER16 +#define U_SIZE_EMRPOLYLINE16 U_SIZE_EMRPOLYBEZIER16 +#define U_SIZE_EMRPOLYBEZIERTO16 U_SIZE_EMRPOLYBEZIER16 +#define U_SIZE_EMRPOLYLINETO16 U_SIZE_EMRPOLYBEZIER16 /* Index 90,91 */ -/** EMF manual 2.3.5.31 +/** EMF manual 2.3.5.31 + + followed by + U_POINT16 apts[1]; //!< array of point16 */ typedef struct { U_EMR emr; //!< U_EMR @@ -2849,24 +2960,28 @@ typedef struct { U_NUM_POLYCOUNTS nPolys; //!< Number of elements in aPolyCounts U_NUM_POINT16 cpts; //!< Total number of points (over all poly) U_POLYCOUNTS aPolyCounts[1]; //!< Number of points in each poly (sequential) -// This will appear somewhere but is not really part of the core structure. -// U_POINT16 apts[1]; //!< array of point16 } U_EMRPOLYPOLYLINE16, U_EMRPOLYPOLYGON16, //!< EMF manual 2.3.5.29 *PU_EMRPOLYPOLYLINE16, //!< EMF manual 2.3.5.31 *PU_EMRPOLYPOLYGON16; //!< EMF manual 2.3.5.29 +#define U_SIZE_EMRPOLYPOLYLINE16 (sizeof(U_EMR) + sizeof(U_RECTL) + sizeof(U_NUM_POLYCOUNTS) + sizeof(U_NUM_POINT16)) +#define U_SIZE_EMRPOLYPOLYGON16 U_SIZE_EMRPOLYPOLYLINE16 /* Index 92 */ -/** EMF manual 2.3.5.21 +/** EMF manual 2.3.5.21 + + followed by + uint8_t abTypes[1]; //!< Array of Point Enumeration + */ typedef struct { U_EMR emr; //!< U_EMR U_RECTL rclBounds; //!< Bounding rectangle in device units U_NUM_POINT16 cpts; //!< Total number of points (over all poly) U_POINT16 apts[1]; //!< array of points - uint8_t abTypes[1]; //!< Array of Point Enumeration } U_EMRPOLYDRAW16, *PU_EMRPOLYDRAW16; //!< EMF manual 2.3.5.21 +#define U_SIZE_EMRPOLYDRAW16 (sizeof(U_EMR) + sizeof(U_RECTL) + sizeof(U_NUM_POINT16)) /* Index 93 */ /** EMF manual 2.3.7.5 @@ -2882,6 +2997,7 @@ typedef struct { //!< Record may include optional DIB bitmapbuffer } U_EMRCREATEMONOBRUSH, *PU_EMRCREATEMONOBRUSH; //!< EMF manual 2.3.7.5 +#define U_SIZE_EMRCREATEMONOBRUSH (sizeof(U_EMRCREATEMONOBRUSH)) /* Index 94 */ /** EMF manual 2.3.7.4 @@ -2897,6 +3013,7 @@ typedef struct { //!< Record may include optional DIB bitmapbuffer } U_EMRCREATEDIBPATTERNBRUSHPT, *PU_EMRCREATEDIBPATTERNBRUSHPT; //!< EMF manual 2.3.7.4 +#define U_SIZE_EMRCREATEDIBPATTERNBRUSHPT (sizeof(U_EMRCREATEDIBPATTERNBRUSHPT)) /* Index 95 */ /** EMF manual 2.3.7.9 @@ -2912,6 +3029,7 @@ typedef struct { //!< Record may include optional DIB bitmap } U_EMREXTCREATEPEN, *PU_EMREXTCREATEPEN; //!< EMF manual 2.3.7.9 +#define U_SIZE_EMREXTCREATEPEN (sizeof(U_EMREXTCREATEPEN)) /* Index 96.97 */ /** EMF manual 2.3.5.32 @@ -2928,6 +3046,8 @@ typedef struct { U_EMRPOLYTEXTOUTW, //!< EMF manual 2.3.5.33 *PU_EMRPOLYTEXTOUTA, //!< EMF manual 2.3.5.32 *PU_EMRPOLYTEXTOUTW; //!< EMF manual 2.3.5.33 +#define U_SIZE_EMRPOLYTEXTOUTA (sizeof(U_EMR) + sizeof(U_RECTL) + sizeof(uint32_t) + 2*sizeof(U_FLOAT) + sizeof(U_NUM_EMRTEXT)) +#define U_SIZE_EMRPOLYTEXTOUTW U_SIZE_EMRPOLYTEXTOUTA /* Index 98 (see 17) */ @@ -2940,6 +3060,7 @@ typedef struct { U_LOGCOLORSPACEA lcs; //!< ColorSpace parameters } U_EMRCREATECOLORSPACE, *PU_EMRCREATECOLORSPACE; //!< EMF manual 2.3.7.2 +#define U_SIZE_EMRCREATECOLORSPACE (sizeof(U_EMRCREATECOLORSPACE)) /* Index 100,101 */ /** EMF manual 2.3.8.2 @@ -2951,6 +3072,8 @@ typedef struct { U_EMRSETCOLORSPACE, //!< EMF manual 2.3.8.7 *PU_EMRDELETECOLORSPACE, //!< EMF manual 2.3.8.2 *PU_EMRSETCOLORSPACE; //!< EMF manual 2.3.8.7 +#define U_SIZE_EMRDELETECOLORSPACE (sizeof(U_EMRDELETECOLORSPACE)) +#define U_SIZE_EMRSETCOLORSPACE (sizeof(U_EMRSETCOLORSPACE )) /* Index 102 */ /** EMF manual 2.3.9.2 @@ -2961,6 +3084,7 @@ typedef struct { U_DATA Data[1]; //!< OpenGL data } U_EMRGLSRECORD, *PU_EMRGLSRECORD; //!< EMF manual 2.3.9.2 +#define U_SIZE_EMRGLSRECORD (sizeof(U_EMRGLSRECORD)) /* Index 103 */ /** EMF manual 2.3.9.1 @@ -2972,6 +3096,7 @@ typedef struct { U_DATA Data[1]; //!< OpenGL data } U_EMRGLSBOUNDEDRECORD, *PU_EMRGLSBOUNDEDRECORD; //!< EMF manual 2.3.9.1 +#define U_SIZE_EMRGLSBOUNDEDRECORD (sizeof(U_EMR) + sizeof(U_RECTL) + sizeof(U_CBDATA)) /* Index 104 */ /** EMF manual 2.3.11.5 @@ -2981,6 +3106,7 @@ typedef struct { U_PIXELFORMATDESCRIPTOR pfd; //!< PixelFormatDescriptor } U_EMRPIXELFORMAT, *PU_EMRPIXELFORMAT; //!< EMF manual 2.3.11.5 +#define U_SIZE_EMRPIXELFORMAT (sizeof(U_EMRPIXELFORMAT)) /* Index 105 */ /** EMF manual 2.3.6.1 @@ -2991,6 +3117,7 @@ typedef struct { U_DATA Data[1]; //!< Data to send } U_EMRDRAWESCAPE, *PU_EMRDRAWESCAPE; //!< EMF manual 2.3.6.1 +#define U_SIZE_EMRDRAWESCAPE (sizeof(U_EMR) + sizeof(U_CBDATA)) /* Index 106 */ /** EMF manual 2.3.6.2 @@ -3001,6 +3128,7 @@ typedef struct { U_DATA Data[1]; //!< Data to send } U_EMREXTESCAPE, *PU_EMREXTESCAPE; //!< EMF manual 2.3.6.2 +#define U_SIZE_EMREXTESCAPE (sizeof(U_EMR) + sizeof(U_CBDATA)) /* Index 107 (not implemented ) */ @@ -3020,22 +3148,26 @@ typedef struct { //!< uint32_t TextString; text to output (fuOptions & ETO_SMALL_CHARS ? 8 bit : 16 bit) } U_EMRSMALLTEXTOUT, *PU_EMRSMALLTEXTOUT; //!< EMF manual 2.3.5.37 +#define U_SIZE_EMRSMALLTEXTOUT (sizeof(U_EMRSMALLTEXTOUT)) /* Index 109 (not implemented ) EMF manual 2.3.11.2 */ /* Index 110 */ -/** EMF manual 2.3.6.3 +/** EMF manual 2.3.6.3 + + followed by + uint8_t Data[1]; //!< Data for printer driver */ typedef struct { U_EMR emr; //!< U_EMR U_CBDATA cbDriver; //!< Number of bytes in driver name (note, BYTES, not CHARACTERS) U_CBDATA cbData; //!< Number of bytes in data uint16_t Driver[1]; //!< Driver name in uint16_t characters, null terminated - uint8_t Data[1]; //!< Data for printer driver } U_EMRNAMEDESCAPE, *PU_EMRNAMEDESCAPE; //!< EMF manual 2.3.6.3 +#define U_SIZE_EMRNAMEDESCAPE (sizeof(U_EMRNAMEDESCAPE)) /* Index 111-113 (not implemented ) EMF manual 2.3.8.1 @@ -3064,6 +3196,7 @@ typedef struct { //!< Record may include optional DIB bitmap } U_EMRALPHABLEND, *PU_EMRALPHABLEND; //!< EMF manual 2.3.1.1 +#define U_SIZE_EMRALPHABLEND (sizeof(U_EMRALPHABLEND)) /* Index 115 (see 17) */ @@ -3088,11 +3221,16 @@ typedef struct { //!< Record may includes optional bitmapbuffer } U_EMRTRANSPARENTBLT, *PU_EMRTRANSPARENTBLT; //!< EMF manual 2.3.1.8 +#define U_SIZE_EMRTRANSPARENTBLT (sizeof(U_EMRTRANSPARENTBLT)) /* Index 117 (not a defined U_EMR record type ) */ /* Index 118 */ -/** EMF manual 2.3.5.12 +/** EMF manual 2.3.5.12 + + followed by + U_TRIVERTEX TriVert[1]; Array of TriVertex objects + uint32_t GradObj[1]; Array of gradient objects (each has 2 or 3 indices into TriVert array) */ typedef struct { U_EMR emr; //!< U_EMR @@ -3100,11 +3238,9 @@ typedef struct { U_NUM_TRIVERTEX nTriVert; //!< Number of TriVertex objects U_NUM_GRADOBJ nGradObj; //!< Number of gradient triangle/rectangle objects uint32_t ulMode; //!< Gradientfill Enumeration (determines Triangle/Rectangle) -//parts that are required but which are not included in the core structure -// U_TRIVERTEX TriVert[1]; Array of TriVertex objects -// uint32_t GradObj[1]; Array of gradient objects (each has 2 or 3 indices into TriVert array) } U_EMRGRADIENTFILL, *PU_EMRGRADIENTFILL; //!< EMF manual 2.3.5.12 +#define U_SIZE_EMRGRADIENTFILL (sizeof(U_EMRGRADIENTFILL)) /* Index 119,120 (not implemented ) EMF manual 2.3.11.18 @@ -3123,6 +3259,7 @@ typedef struct { uint8_t Data[1]; //!< Data of size cbName+cbData: Name in UTF16 then color profile data } U_EMRCOLORMATCHTOTARGETW, *PU_EMRCOLORMATCHTOTARGETW; //!< EMF manual 2.3.11.1 +#define U_SIZE_EMRCOLORMATCHTOTARGETW (sizeof(U_EMR) + 2*sizeof(uint32_t) + sizeof(U_CBNAME) + sizeof(U_CBDATA)) /* Index 122 */ /** EMF manual 2.3.7.3 @@ -3136,6 +3273,10 @@ typedef struct { uint8_t Data[1]; //!< (Optional, dwFlags & 1) color profile data } U_EMRCREATECOLORSPACEW, *PU_EMRCREATECOLORSPACEW; //!< EMF manual 2.3.7.3 +#define U_SIZE_EMRCREATECOLORSPACEW (sizeof(U_EMR) + 2*sizeof(uint32_t) + sizeof(U_LOGCOLORSPACEW) + sizeof(U_CBDATA)) + +// records which are not documented, so we have no idea what to do with them +#define U_SIZE_EMRUNDEFINED 2048 // ************************************************************************************************ // Utility function structures @@ -3207,7 +3348,7 @@ int get_real_color_count(const char *Bmih); int get_real_color_icount(int Colors, int BitCount, int Width, int Height); int RGBA_to_DIB(char **px, uint32_t *cbPx, PU_RGBQUAD *ct, int *numCt, const char *rgba_px, int w, int h, int stride, uint32_t colortype, int use_ct, int invert); -int get_DIB_params( void *pEmr, uint32_t offBitsSrc, uint32_t offBmiSrc, +int get_DIB_params( const char *record, uint32_t offBitsSrc, uint32_t offBmiSrc, const char **px, const U_RGBQUAD **ct, uint32_t *numCt, uint32_t *width, uint32_t *height, uint32_t *colortype, uint32_t *invert ); int DIB_to_RGBA(const char *px, const U_RGBQUAD *ct, int numCt, @@ -3237,6 +3378,7 @@ int emf_htable_free(EMFHANDLES **eht); #define htable_free emf_htable_free U_RECTL rectl_set(U_POINTL ul, U_POINTL lr); +void rectli_set(PU_RECTL array, int index, U_POINTL ul, U_POINTL lr); U_SIZEL sizel_set(int32_t x, int32_t y); U_POINTL point32_set(int32_t x, int32_t y); #define point_set point32_set @@ -3463,6 +3605,7 @@ char *U_EMRSMALLTEXTOUT_set(const U_POINTL Dest, const U_NUM_STR cChars, const u // U_EMRSETICMPROFILEA_set 112 Not implemented // U_EMRSETICMPROFILEW_set 113 Not implemented + char *U_EMRALPHABLEND_set(const U_RECTL rclBounds, const U_POINTL Dest, const U_POINTL cDest, const U_POINTL Src, const U_POINTL cSrc, const U_XFORM xformSrc, const U_COLORREF crBkColorSrc, const uint32_t iUsageSrc, diff --git a/src/libuemf/uemf_endian.c b/src/libuemf/uemf_endian.c index 2d19361e1..f746553f1 100644 --- a/src/libuemf/uemf_endian.c +++ b/src/libuemf/uemf_endian.c @@ -19,11 +19,11 @@ /* File: uemf_endian.c -Version: 0.0.16 -Date: 27-MAR-2014 +Version: 0.0.21 +Date: 23-APR-2015 Author: David Mathog, Biology Division, Caltech email: mathog@caltech.edu -Copyright: 2014 David Mathog and California Institute of Technology (Caltech) +Copyright: 2015 David Mathog and California Institute of Technology (Caltech) */ #ifdef __cplusplus @@ -266,18 +266,91 @@ void bitmapinfoheader_swap( \param Bmi Pointer to a U_BITMAPINFO object */ void bitmapinfo_swap( - PU_BITMAPINFO Bmi + const char *Bmi ){ - bitmapinfoheader_swap(&(Bmi->bmiHeader)); // bmIHeader + PU_BITMAPINFO pBmi = (PU_BITMAPINFO)Bmi; + bitmapinfoheader_swap(&(pBmi->bmiHeader)); // bmIHeader // ordered bytes: bmiColors } /** + \brief Swap the ordered bytes in a DIB and verify that the sizes are OK + + \return 1 on success, 0 on failure + \param record EMF record that contains a DIB pixel array + \param iUsage DIBcolors Enumeration + \param offBmi offset from the start of the record to the start of the bitmapinfo structure + \param cbBmi declared space for the bitmapinfo structure in the record + \param offBits offset from the start of the record to the start of the bitmap + \param cbBits declared space for the bitmap in the record (amount used may be less than this) + \param blimit one byte past the end of the record. + \param torev 1 for native to reversed, 0 for reversed to native + + This method can only test DIBs that hold Microsoft's various bitmap types. PNG or JPG is just a bag + of bytes, and there is no possible way to derive from the known width and height how big it should be. +*/ +int DIB_swap( + const char *record, + uint32_t iUsage, + uint32_t offBmi, + uint32_t cbBmi, + uint32_t offBits, + uint32_t cbBits, + const char *blimit, + int torev + ){ + int dibparams = U_BI_UNKNOWN; // type of image not yet determined + const char *px = NULL; // DIB pixels + const U_RGBQUAD *ct = NULL; // DIB color table + int bs; + int usedbytes; + + if(!cbBmi)return(1); // No DIB in a record where it is optional + if(IS_MEM_UNSAFE(record, offBmi + cbBmi, blimit))return(0); + if(cbBits && IS_MEM_UNSAFE(record, offBits + cbBits, blimit))return(0); + if(iUsage == U_DIB_RGB_COLORS){ + uint32_t width, height, colortype, numCt, invert; // these values will be set in get_DIB_params + // next call returns pointers and values, but allocates no memory + if(torev){ + dibparams = get_DIB_params(record, offBits, offBmi, &px, (const U_RGBQUAD **) &ct, + &numCt, &width, &height, &colortype, &invert); + } + bitmapinfo_swap(record + offBmi); // byte ordered fields in bitmapinfo + if(!torev){ + dibparams = get_DIB_params(record, offBits, offBmi, &px, (const U_RGBQUAD **) &ct, + &numCt, &width, &height, &colortype, &invert); + } + + // sanity checking + if(numCt && colortype >= U_BCBM_COLOR16)return(0); //color tables not used above 16 bit pixels + if(!numCt && colortype < U_BCBM_COLOR16)return(0); //color tables mandatory for < 16 bit + + if(dibparams ==U_BI_RGB){ + // this is the only DIB type where we can calculate how big it should be when stored in the EMF file + bs = colortype/8; + if(bs<1){ + usedbytes = (width*colortype + 7)/8; // width of line in fully and partially occupied bytes + } + else { + usedbytes = width*bs; + } + if(IS_MEM_UNSAFE(record+offBits, usedbytes, blimit))return(0); + } + } + else { + bitmapinfo_swap(record + offBmi); + } + return(1); +} + +/** \brief Convert a pointer to a U_EXTLOGPEN object. - \param elp PU_EXTLOGPEN object + \param elp PU_EXTLOGPEN object + \param blimit one byte past the end of the record */ -void extlogpen_swap( +int extlogpen_swap( PU_EXTLOGPEN elp, + const char *blimit, int torev ){ int count=0; @@ -290,7 +363,9 @@ void extlogpen_swap( if(!torev){ count = elp->elpNumEntries; } + if(IS_MEM_UNSAFE(&(elp->elpStyleEntry), count*4, blimit))return(0); U_swap4(&(elp->elpStyleEntry),count); // elpStyleEntry[] + return(1); } /** @@ -330,15 +405,27 @@ void rgndataheader_swap( } /** + \return 1 on success, 0 on failure \brief Convert a pointer to a U_RGNDATA object. - \param rgd pointer to a U_RGNDATA object. + \param rd pointer to a U_RGNDATA object. + \param cbRgnData size of the U_RGNDATA object. */ -void rgndata_swap( - PU_RGNDATA rd +int rgndata_swap( + PU_RGNDATA rd, + int cbRgnData, + int torev ){ - int count = rd->rdh.nCount; + int count = 0; + if(torev){ + count = rd->rdh.nCount; + } rgndataheader_swap(&(rd->rdh)); + if(!torev){ + count = rd->rdh.nCount; + } + if(4*count + (int)sizeof(U_RGNDATAHEADER) > cbRgnData)return(0); U_swap4(rd->Buffer,4*count); + return(1); } /** @@ -368,12 +455,14 @@ void pixelformatdescriptor_swap( \brief Convert a Pointer to a U_EMRTEXT record \param pemt Pointer to a U_EMRTEXT record \param record Pointer to the start of the record which contains this U_EMRTEXT + \param blimit one byte past the end of the record. \param torev 1 for native to reversed, 0 for reversed to native */ -void emrtext_swap( - PU_EMRTEXT pemt, - char *record, - int torev +int emrtext_swap( + PU_EMRTEXT pemt, + char *record, + const char *blimit, + int torev ){ int off; uint32_t count=0; @@ -391,6 +480,7 @@ void emrtext_swap( } off = sizeof(U_EMRTEXT); if(!(fOptions & U_ETO_NO_RECT)){ + if(IS_MEM_UNSAFE(pemt, sizeof(U_RECTL), blimit))return(0); rectl_swap((PU_RECTL)((char *)pemt + off),1); // optional rectangle off+=sizeof(U_RECTL); } @@ -398,11 +488,14 @@ void emrtext_swap( offDx = *(uint32_t *)((char *)pemt +off); } // ordered bytes OR UTF16-LE: the string at offString + if(IS_MEM_UNSAFE(pemt, off + 4, blimit))return(0); U_swap4(((char *)pemt+off),1); // offDx if(!torev){ offDx = *(uint32_t *)((char *)pemt +off); } - U_swap4((record+offDx),count); // Dx[], offset with respect to the Record, NOT the object + if(IS_MEM_UNSAFE(record, count*4, blimit))return(0); + U_swap4((record + offDx),count); // Dx[], offset with respect to the Record, NOT the object + return(1); } @@ -424,182 +517,236 @@ by end user code and to further that end prototypes are NOT provided and they ar // all core*_swap call this, U_EMRSETMARGN_swap and some others all it directly // numbered as core5 to be consistent with uemf.c, but must appear before the others as there is no prototype -void core5_swap(char *record, int torev){ +int core5_swap(char *record, int torev){ UNUSED_PARAMETER(torev); - PU_ENHMETARECORD pEMR = (PU_ENHMETARECORD)(record); - U_swap4(pEMR,2); // iType nSize + if(!record)return(0); + PU_EMR pEmr = (PU_EMR)(record); + U_swap4(pEmr,2); // iType nSize + return(1); } // Functions with the same form starting with U_EMRPOLYBEZIER_swap -void core1_swap(char *record, int torev){ +int core1_swap(char *record, int torev){ int count=0; + const char *blimit = NULL; PU_EMRPOLYLINETO pEmr = (PU_EMRPOLYLINETO) (record); if(torev){ count = pEmr->cptl; + blimit = record + pEmr->emr.nSize; } - core5_swap(record, torev); + if(!core5_swap(record, torev))return(0); rectl_swap(&(pEmr->rclBounds),1 ); // rclBounds U_swap4(&(pEmr->cptl),1); // cptl if(!torev){ count = pEmr->cptl; + blimit = record + pEmr->emr.nSize; } + if(IS_MEM_UNSAFE((pEmr->aptl), count*sizeof(U_POINTL), blimit))return(0); pointl_swap((pEmr->aptl),count); // aptl[] + return(1); } // Functions with the same form starting with U_EMRPOLYPOLYLINE_swap -void core2_swap(char *record, int torev){ +int core2_swap(char *record, int torev){ int count=0; int nPolys=0; + const char *blimit = NULL; PU_EMRPOLYPOLYLINE pEmr = (PU_EMRPOLYPOLYLINE) (record); if(torev){ count = pEmr->cptl; nPolys = pEmr->nPolys; + blimit = record + pEmr->emr.nSize; } - core5_swap(record, torev); + if(!core5_swap(record, torev))return(0); rectl_swap(&(pEmr->rclBounds),1); // rclBounds U_swap4(&(pEmr->nPolys),2); // nPolys cptl if(!torev){ count = pEmr->cptl; nPolys = pEmr->nPolys; + blimit = record + pEmr->emr.nSize; } + if(IS_MEM_UNSAFE((pEmr->aPolyCounts), nPolys*4, blimit))return(0); U_swap4(pEmr->aPolyCounts,nPolys); // aPolyCounts[] - pointl_swap((PU_POINT)(record + sizeof(U_EMRPOLYPOLYLINE) - 4 + sizeof(uint32_t)* nPolys), count); // paptl[] + record += sizeof(U_EMRPOLYPOLYLINE) - 4 + sizeof(uint32_t)* nPolys; + if(IS_MEM_UNSAFE(record, count*sizeof(U_POINTL), blimit))return(0); + pointl_swap((PU_POINT)(record), count); // paptl[] + return(1); } // Functions with the same form starting with U_EMRSETMAPMODE_swap -void core3_swap(char *record, int torev){ +int core3_swap(char *record, int torev){ PU_EMRSETMAPMODE pEmr = (PU_EMRSETMAPMODE)(record); - core5_swap(record, torev); + if(!core5_swap(record, torev))return(0); U_swap4(&(pEmr->iMode),1); // iMode + return(1); } // Functions taking a single U_RECT or U_RECTL, starting with U_EMRELLIPSE_swap, also U_EMRFILLPATH_swap, -void core4_swap(char *record, int torev){ +int core4_swap(char *record, int torev){ PU_EMRELLIPSE pEmr = (PU_EMRELLIPSE)( record); - core5_swap(record, torev); + if(!core5_swap(record, torev))return(0); rectl_swap(&(pEmr->rclBox),1); // rclBox + return(1); } // Functions with the same form starting with U_EMRPOLYBEZIER16_swap -void core6_swap(char *record, int torev){ +int core6_swap(char *record, int torev){ int count=0; + const char *blimit = NULL; PU_EMRPOLYBEZIER16 pEmr = (PU_EMRPOLYBEZIER16) (record); if(torev){ count = pEmr->cpts; + blimit = record + pEmr->emr.nSize; } - core5_swap(record, torev); + if(!core5_swap(record, torev))return(0); rectl_swap(&(pEmr->rclBounds),1); // rclBounds U_swap4(&(pEmr->cpts),1); // cpts if(!torev){ count = pEmr->cpts; + blimit = record + pEmr->emr.nSize; } + if(IS_MEM_UNSAFE((pEmr->apts), count*sizeof(U_POINT16), blimit))return(0); point16_swap((pEmr->apts),count); // apts[] + return(1); } // Records with the same form starting with U_EMRSETWINDOWEXTEX_swap, that is, all with two uint32_t values after the emr -void core7_swap(char *record, int torev){ +int core7_swap(char *record, int torev){ PU_EMRGENERICPAIR pEmr = (PU_EMRGENERICPAIR) (record); - core5_swap(record, torev); + if(!core5_swap(record, torev))return(0); U_swap4(&(pEmr->pair),2); + return(1); } // For U_EMREXTTEXTOUTA and U_EMREXTTEXTOUTW, type=0 for the first one -void core8_swap(char *record, int torev){ +int core8_swap(char *record, int torev){ + const char *blimit = NULL; PU_EMREXTTEXTOUTA pEmr = (PU_EMREXTTEXTOUTA) (record); - emrtext_swap(&(pEmr->emrtext),record,torev); - core5_swap(record, torev); + if(torev){ + blimit = record + pEmr->emr.nSize; + } + if(!core5_swap(record, torev))return(0); U_swap4(&(pEmr->iGraphicsMode),1); // iGraphicsMode rectl_swap(&(pEmr->rclBounds),1); // rclBounds U_swap4(&(pEmr->exScale),2); // exScale eyScale + if(!torev){ + blimit = record + pEmr->emr.nSize; + } + if(!emrtext_swap(&(pEmr->emrtext),record,blimit,torev))return(0); + return(1); } // Functions that take a rect and a pair of points, starting with U_EMRARC_swap -void core9_swap(char *record, int torev){ +int core9_swap(char *record, int torev){ PU_EMRARC pEmr = (PU_EMRARC) (record); - core5_swap(record, torev); + if(!core5_swap(record, torev))return(0); rectl_swap(&(pEmr->rclBox),1); // rclBox U_swap4(&(pEmr->ptlStart),4); // ptlStart ptlEnd + return(1); } // Functions with the same form starting with U_EMRPOLYPOLYLINE16_swap -void core10_swap(char *record, int torev){ +int core10_swap(char *record, int torev){ int count=0; int nPolys=0; + const char *blimit = NULL; PU_EMRPOLYPOLYLINE16 pEmr = (PU_EMRPOLYPOLYLINE16) (record); if(torev){ count = pEmr->cpts; nPolys = pEmr->nPolys; + blimit = record + pEmr->emr.nSize; } - core5_swap(record, torev); + if(!core5_swap(record, torev))return(0); rectl_swap(&(pEmr->rclBounds),1); // rclBounds U_swap4(&(pEmr->nPolys),2); // nPolys cpts if(!torev){ count = pEmr->cpts; nPolys = pEmr->nPolys; + blimit = record + pEmr->emr.nSize; } + if(IS_MEM_UNSAFE((pEmr->aPolyCounts), nPolys*4, blimit))return(0); U_swap4(pEmr->aPolyCounts,nPolys); // aPolyCounts[] - point16_swap((PU_POINT16)(record + sizeof(U_EMRPOLYPOLYLINE16) - 4 + sizeof(uint32_t)* nPolys), count); // apts[] + record += sizeof(U_EMRPOLYPOLYLINE16) - 4 + sizeof(uint32_t)* nPolys; + if(IS_MEM_UNSAFE(record, count*sizeof(U_POINT16), blimit))return(0); + point16_swap((PU_POINT16)(record), count); // apts[] + return(1); } // Functions with the same form starting with U_EMRINVERTRGN_swap and U_EMRPAINTRGN_swap, -void core11_swap(char *record, int torev){ - int roff=0; - int nextroff=0; - int limit=0; - PU_EMRINVERTRGN pEmr = (PU_EMRINVERTRGN) (record); - roff = 0; +int core11_swap(char *record, int torev){ + int cbRgnData=0; + const char *blimit = NULL; + PU_EMRINVERTRGN pEmr = (PU_EMRINVERTRGN)(record); if(torev){ - limit = pEmr->emr.nSize; - nextroff = 0; - } - core5_swap(record, torev); - if(!torev){ - limit = pEmr->emr.nSize; + cbRgnData= pEmr->cbRgnData; + blimit = record + pEmr->emr.nSize; } + if(!core5_swap(record, torev))return(0); rectl_swap(&(pEmr->rclBounds),1); // rclBounds U_swap4(&(pEmr->cbRgnData),1); // cbRgnData if(!torev){ - limit = pEmr->emr.nSize; - } - // This one is a pain since each RGNDATA may be a different size, so it isn't possible to index through them. - char *prd = (char *) &(pEmr->RgnData); - while(roff + 28 < limit){ // up to the end of the record - if(torev){ - nextroff += (((PU_RGNDATA)prd)->rdh.dwSize + ((PU_RGNDATA)prd)->rdh.nRgnSize - 16); - rgndata_swap((PU_RGNDATA) (prd + roff)); - roff = nextroff; - } - else { - rgndata_swap((PU_RGNDATA) (prd + roff)); - roff += (((PU_RGNDATA)prd)->rdh.dwSize + ((PU_RGNDATA)prd)->rdh.nRgnSize - 16); - } + cbRgnData= pEmr->cbRgnData; + blimit = record + pEmr->emr.nSize; } + if(IS_MEM_UNSAFE(pEmr->RgnData, cbRgnData, blimit))return(0); + return(rgndata_swap(pEmr->RgnData, cbRgnData, torev)); } // common code for U_EMRCREATEMONOBRUSH_swap and U_EMRCREATEDIBPATTERNBRUSHPT_swap, -void core12_swap(char *record, int torev){ +int core12_swap(char *record, int torev){ + const char *blimit = NULL; + U_OFFBMI offBmi = 0; + U_CBBMI cbBmi = 0; + U_OFFBITS offBits = 0; + U_CBBITS cbBits = 0; + uint32_t iUsage = 0; PU_EMRCREATEMONOBRUSH pEmr = (PU_EMRCREATEMONOBRUSH) (record); - if(torev && pEmr->cbBmi){ - bitmapinfo_swap((PU_BITMAPINFO)(record + pEmr->offBmi)); // Bmi - } - core5_swap(record, torev); + if(torev){ + offBmi = pEmr->offBmi; + cbBmi = pEmr->cbBmi; + offBits = pEmr->offBits; + cbBits = pEmr->cbBits; + iUsage = pEmr->iUsage; + blimit = record + pEmr->emr.nSize; + if(!DIB_swap(record, iUsage, offBmi, cbBmi, offBits, cbBits, blimit, torev))return(0); + } + if(!core5_swap(record, torev))return(0); U_swap4(&(pEmr->ihBrush),6); // ihBrush iUsage offBmi cbBmi offBits cbBits - // ordered bytes: bitmap (including 16 bit 5bit/channel color mode, which is done bytewise). - if(!torev && pEmr->cbBmi){ - bitmapinfo_swap((PU_BITMAPINFO)(record + pEmr->offBmi)); // Bmi + if(!torev){ + offBmi = pEmr->offBmi; + cbBmi = pEmr->cbBmi; + offBits = pEmr->offBits; + cbBits = pEmr->cbBits; + iUsage = pEmr->iUsage; + blimit = record + pEmr->emr.nSize; + if(!DIB_swap(record, iUsage, offBmi, cbBmi, offBits, cbBits, blimit, torev))return(0); } + // ordered bytes: bitmap (including 16 bit 5bit/channel color mode, which is done bytewise). + return(1); } // common code for U_EMRALPHABLEND_swap and U_EMRTRANSPARENTBLT_swap, -void core13_swap(char *record, int torev){ +int core13_swap(char *record, int torev){ + const char *blimit = NULL; + U_OFFBMISRC offBmiSrc = 0; + U_CBBMISRC cbBmiSrc = 0; + U_OFFBITSSRC offBitsSrc = 0; + U_CBBITSSRC cbBitsSrc = 0; + uint32_t iUsageSrc = 0; PU_EMRALPHABLEND pEmr = (PU_EMRALPHABLEND) (record); - if(torev && pEmr->cbBmiSrc){ - bitmapinfo_swap((PU_BITMAPINFO)(record + pEmr->offBmiSrc)); - } - core5_swap(record, torev); + if(torev){ + offBmiSrc = pEmr->offBmiSrc; + cbBmiSrc = pEmr->cbBmiSrc; + offBitsSrc = pEmr->offBitsSrc; + cbBitsSrc = pEmr->cbBitsSrc; + iUsageSrc = pEmr->iUsageSrc; + blimit = record + pEmr->emr.nSize; + if(!DIB_swap(record, iUsageSrc, offBmiSrc, cbBmiSrc, offBitsSrc, cbBitsSrc, blimit, torev))return(0); + } + if(!core5_swap(record, torev))return(0); rectl_swap(&(pEmr->rclBounds),1); // rclBounds pointl_swap(&(pEmr->Dest),2); // Dest cDest pointl_swap(&(pEmr->Dest),2); // Dest cDest @@ -608,15 +755,25 @@ void core13_swap(char *record, int torev){ xform_swap( &(pEmr->xformSrc)); // xformSrc // ordered bytes: crBkColorSrc U_swap4(&(pEmr->iUsageSrc),5); // iUsageSrc offBmiSrc cbBmiSrc offBitsSrc cbBitsSrc - // ordered bytes: bitmap (including 16 bit 5bit/channel color mode, which is done bytewise). - if(!torev && pEmr->cbBmiSrc){ - bitmapinfo_swap((PU_BITMAPINFO)(record + pEmr->offBmiSrc)); + if(!torev){ + offBmiSrc = pEmr->offBmiSrc; + cbBmiSrc = pEmr->cbBmiSrc; + offBitsSrc = pEmr->offBitsSrc; + cbBitsSrc = pEmr->cbBitsSrc; + iUsageSrc = pEmr->iUsageSrc; + blimit = record + pEmr->emr.nSize; + if(!DIB_swap(record, iUsageSrc, offBmiSrc, cbBmiSrc, offBitsSrc, cbBitsSrc, blimit, torev))return(0); } + // ordered bytes: bitmap (including 16 bit 5bit/channel color mode, which is done bytewise). + return(1); } /* ********************************************************************************************** -These are the core EMR functions, each creates a particular type of record. -All return these records via a char* pointer, which is NULL if the call failed. +These are the core EMR_swap functions, each converts a particular type of record. +All operate in place on the chunk of memory holding that record. +Some of these have offsets or counts which, if corrupt or evil would result in access outside + the record. These cases return a status value of 0 if that happens, 1 on success. Other + records which do not have these issues do not return a status value. They are listed in order by the corresponding U_EMR_* index number. *********************************************************************************************** */ @@ -626,37 +783,31 @@ They are listed in order by the corresponding U_EMR_* index number. \param record pointer to a buffer holding the EMR record \param torev 1 for native to reversed, 0 for reversed to native */ -void U_EMRNOTIMPLEMENTED_swap(char *record, int torev){ - core5_swap(record, torev); - printf("WARNING: could not convert data in record type that has not been implemented!\n"); +int U_EMRNOTIMPLEMENTED_swap(char *record, int torev){ + fprintf(stderr,"EMF WARNING: could not swap data bytes on record because that type has not been implemented!\n"); + return(core5_swap(record, torev)); } // U_EMRHEADER 1 -void U_EMRHEADER_swap(char *record, int torev){ +int U_EMRHEADER_swap(char *record, int torev){ int nDesc,offDesc,nSize,cbPix,offPix; + nDesc = offDesc = nSize = cbPix = offPix = 0; PU_EMRHEADER pEmr = (PU_EMRHEADER)(record); if(torev){ nSize = pEmr->emr.nSize; - core5_swap(record, torev); - } - else { - core5_swap(record, torev); - nSize = pEmr->emr.nSize; + nDesc = pEmr->nDescription; + offDesc = pEmr->offDescription; } - + if(!core5_swap(record, torev))return(0); rectl_swap(&(pEmr->rclBounds),2); // rclBounds rclFrame U_swap4(&(pEmr->dSignature), 4); // dSignature nVersion nBytes nRecords U_swap2(&(pEmr->nHandles), 2); // nHandlessReserved - if(torev){ - nDesc = pEmr->nDescription; - offDesc = pEmr->offDescription; - U_swap4(&(pEmr->nDescription), 3); // nDescription offDescription nPalEntries - } - else { - U_swap4(&(pEmr->nDescription), 3); // nDescription offDescription nPalEntries - nDesc = pEmr->nDescription; - offDesc = pEmr->offDescription; - } + U_swap4(&(pEmr->nDescription), 3); // nDescription offDescription nPalEntries + if(!torev){ + nSize = pEmr->emr.nSize; + nDesc = pEmr->nDescription; + offDesc = pEmr->offDescription; + } // UTF16-LE Description sizel_swap(&(pEmr->szlDevice), 2); // szlDevice szlMillimeters if((nDesc && (offDesc >= 100)) || @@ -665,16 +816,14 @@ void U_EMRHEADER_swap(char *record, int torev){ if(torev){ cbPix = pEmr->cbPixelFormat; offPix = pEmr->offPixelFormat; - if(cbPix)pixelformatdescriptor_swap( (PU_PIXELFORMATDESCRIPTOR) (record + pEmr->offPixelFormat)); - U_swap4(&(pEmr->cbPixelFormat), 2); // cbPixelFormat offPixelFormat } - else { - U_swap4(&(pEmr->cbPixelFormat), 2); // cbPixelFormat offPixelFormat + U_swap4(&(pEmr->cbPixelFormat), 2); // cbPixelFormat offPixelFormat + U_swap4(&(pEmr->bOpenGL), 1); // bOpenGL + if(!torev){ cbPix = pEmr->cbPixelFormat; offPix = pEmr->offPixelFormat; - if(cbPix)pixelformatdescriptor_swap( (PU_PIXELFORMATDESCRIPTOR) (record + pEmr->offPixelFormat)); } - U_swap4(&(pEmr->bOpenGL), 1); // bOpenGL + if(cbPix)pixelformatdescriptor_swap( (PU_PIXELFORMATDESCRIPTOR) (record + pEmr->offPixelFormat)); if((nDesc && (offDesc >= 108)) || (cbPix && (offPix >=108)) || (!offDesc && !cbPix && nSize >= 108) @@ -682,543 +831,557 @@ void U_EMRHEADER_swap(char *record, int torev){ sizel_swap(&(pEmr->szlMicrometers), 1); // szlMicrometers } } + return(1); } // U_EMRPOLYBEZIER 2 -void U_EMRPOLYBEZIER_swap(char *record, int torev){ - core1_swap(record, torev); +int U_EMRPOLYBEZIER_swap(char *record, int torev){ + return(core1_swap(record, torev)); } // U_EMRPOLYGON 3 -void U_EMRPOLYGON_swap(char *record, int torev){ - core1_swap(record, torev); +int U_EMRPOLYGON_swap(char *record, int torev){ + return(core1_swap(record, torev)); } // U_EMRPOLYLINE 4 -void U_EMRPOLYLINE_swap(char *record, int torev){ - core1_swap(record, torev); +int U_EMRPOLYLINE_swap(char *record, int torev){ + return(core1_swap(record, torev)); } // U_EMRPOLYBEZIERTO 5 -void U_EMRPOLYBEZIERTO_swap(char *record, int torev){ - core1_swap(record, torev); +int U_EMRPOLYBEZIERTO_swap(char *record, int torev){ + return(core1_swap(record, torev)); } // U_EMRPOLYLINETO 6 -void U_EMRPOLYLINETO_swap(char *record, int torev){ - core1_swap(record, torev); +int U_EMRPOLYLINETO_swap(char *record, int torev){ + return(core1_swap(record, torev)); } // U_EMRPOLYPOLYLINE 7 -void U_EMRPOLYPOLYLINE_swap(char *record, int torev){ - core2_swap(record, torev); +int U_EMRPOLYPOLYLINE_swap(char *record, int torev){ + return(core2_swap(record, torev)); } // U_EMRPOLYPOLYGON 8 -void U_EMRPOLYPOLYGON_swap(char *record, int torev){ - core2_swap(record, torev); +int U_EMRPOLYPOLYGON_swap(char *record, int torev){ + return(core2_swap(record, torev)); } // U_EMRSETWINDOWEXTEX 9 -void U_EMRSETWINDOWEXTEX_swap(char *record, int torev){ - core7_swap(record, torev); +int U_EMRSETWINDOWEXTEX_swap(char *record, int torev){ + return(core7_swap(record, torev)); } // U_EMRSETWINDOWORGEX 10 -void U_EMRSETWINDOWORGEX_swap(char *record, int torev){ - core7_swap(record, torev); +int U_EMRSETWINDOWORGEX_swap(char *record, int torev){ + return(core7_swap(record, torev)); } // U_EMRSETVIEWPORTEXTEX 11 -void U_EMRSETVIEWPORTEXTEX_swap(char *record, int torev){ - core7_swap(record, torev); +int U_EMRSETVIEWPORTEXTEX_swap(char *record, int torev){ + return(core7_swap(record, torev)); } // U_EMRSETVIEWPORTORGEX 12 -void U_EMRSETVIEWPORTORGEX_swap(char *record, int torev){ - core7_swap(record, torev); +int U_EMRSETVIEWPORTORGEX_swap(char *record, int torev){ + return(core7_swap(record, torev)); } // U_EMRSETBRUSHORGEX 13 -void U_EMRSETBRUSHORGEX_swap(char *record, int torev){ - core7_swap(record, torev); +int U_EMRSETBRUSHORGEX_swap(char *record, int torev){ + return(core7_swap(record, torev)); } // U_EMREOF 14 -void U_EMREOF_swap(char *record, int torev){ +int U_EMREOF_swap(char *record, int torev){ int off=0; int cbPalEntries=0; - core5_swap(record, torev); + const char *blimit = NULL; PU_EMREOF pEmr = (PU_EMREOF)(record); if(torev){ + blimit = record + pEmr->emr.nSize; cbPalEntries = pEmr->cbPalEntries; - if(cbPalEntries){ - logpalette_swap( (PU_LOGPALETTE)(record + pEmr->offPalEntries)); - } } + if(!core5_swap(record, torev))return(0); U_swap4(&(pEmr->cbPalEntries),2); // cbPalEntries offPalEntries if(!torev){ + blimit = record + pEmr->emr.nSize; cbPalEntries = pEmr->cbPalEntries; - if(cbPalEntries){ - logpalette_swap( (PU_LOGPALETTE)(record + pEmr->offPalEntries)); - } + } + if(cbPalEntries){ + if(IS_MEM_UNSAFE(record, pEmr->offPalEntries + 2*2, blimit))return(0); // 2 16 bit values in U_LOGPALLETE + logpalette_swap( (PU_LOGPALETTE)(record + pEmr->offPalEntries)); + // U_LOGPLTNTRY values in pallette are ordered data } off = sizeof(U_EMREOF) + 4 * cbPalEntries; + if(IS_MEM_UNSAFE(record, off + 4, blimit))return(0); U_swap4(record + off,1); // nSizeLast + return(1); } // U_EMRSETPIXELV 15 -void U_EMRSETPIXELV_swap(char *record, int torev){ - core5_swap(record, torev); +int U_EMRSETPIXELV_swap(char *record, int torev){ + if(!core5_swap(record, torev))return(0); PU_EMRSETPIXELV pEmr = (PU_EMRSETPIXELV)(record); pointl_swap(&(pEmr->ptlPixel),1); // ptlPixel // ordered bytes: crColor + return(1); } // U_EMRSETMAPPERFLAGS 16 -void U_EMRSETMAPPERFLAGS_swap(char *record, int torev){ - core5_swap(record, torev); +int U_EMRSETMAPPERFLAGS_swap(char *record, int torev){ + if(!core5_swap(record, torev))return(0); PU_EMRSETMAPPERFLAGS pEmr = (PU_EMRSETMAPPERFLAGS)(record); U_swap4(&(pEmr->dwFlags),1); // dwFlags + return(1); } // U_EMRSETMAPMODE 17 -void U_EMRSETMAPMODE_swap(char *record, int torev){ - core3_swap(record, torev); +int U_EMRSETMAPMODE_swap(char *record, int torev){ + return(core3_swap(record, torev)); } // U_EMRSETBKMODE 18 -void U_EMRSETBKMODE_swap(char *record, int torev){ - core3_swap(record, torev); +int U_EMRSETBKMODE_swap(char *record, int torev){ + return(core3_swap(record, torev)); } // U_EMRSETPOLYFILLMODE 19 -void U_EMRSETPOLYFILLMODE_swap(char *record, int torev){ - core3_swap(record, torev); +int U_EMRSETPOLYFILLMODE_swap(char *record, int torev){ + return(core3_swap(record, torev)); } // U_EMRSETROP2 20 -void U_EMRSETROP2_swap(char *record, int torev){ - core3_swap(record, torev); +int U_EMRSETROP2_swap(char *record, int torev){ + return(core3_swap(record, torev)); } // U_EMRSETSTRETCHBLTMODE 21 -void U_EMRSETSTRETCHBLTMODE_swap(char *record, int torev){ - core3_swap(record, torev); +int U_EMRSETSTRETCHBLTMODE_swap(char *record, int torev){ + return(core3_swap(record, torev)); } // U_EMRSETTEXTALIGN 22 -void U_EMRSETTEXTALIGN_swap(char *record, int torev){ - core3_swap(record, torev); +int U_EMRSETTEXTALIGN_swap(char *record, int torev){ + return(core3_swap(record, torev)); } // U_EMRSETCOLORADJUSTMENT 23 -void U_EMRSETCOLORADJUSTMENT_swap(char *record, int torev){ - core5_swap(record, torev); +int U_EMRSETCOLORADJUSTMENT_swap(char *record, int torev){ + if(!core5_swap(record, torev))return(0); PU_EMRSETCOLORADJUSTMENT pEmr = (PU_EMRSETCOLORADJUSTMENT)(record); coloradjustment_swap(&(pEmr->ColorAdjustment)); + return(1); } // U_EMRSETTEXTCOLOR 24 -void U_EMRSETTEXTCOLOR_swap(char *record, int torev){ - core5_swap(record, torev); +int U_EMRSETTEXTCOLOR_swap(char *record, int torev){ + if(!core5_swap(record, torev))return(0); // ordered bytes: crColor + return(1); } // U_EMRSETBKCOLOR 25 -void U_EMRSETBKCOLOR_swap(char *record, int torev){ - core5_swap(record, torev); +int U_EMRSETBKCOLOR_swap(char *record, int torev){ + if(!core5_swap(record, torev))return(0); // ordered bytes: crColor + return(1); } // U_EMROFFSETCLIPRGN 26 -void U_EMROFFSETCLIPRGN_swap(char *record, int torev){ - core7_swap(record, torev); +int U_EMROFFSETCLIPRGN_swap(char *record, int torev){ + return(core7_swap(record, torev)); } // U_EMRMOVETOEX 27 -void U_EMRMOVETOEX_swap(char *record, int torev){ - core7_swap(record, torev); +int U_EMRMOVETOEX_swap(char *record, int torev){ + return(core7_swap(record, torev)); } // U_EMRSETMETARGN 28 -void U_EMRSETMETARGN_swap(char *record, int torev){ - core5_swap(record, torev); +int U_EMRSETMETARGN_swap(char *record, int torev){ + return(core5_swap(record, torev)); } // U_EMREXCLUDECLIPRECT 29 -void U_EMREXCLUDECLIPRECT_swap(char *record, int torev){ - core4_swap(record, torev); +int U_EMREXCLUDECLIPRECT_swap(char *record, int torev){ + return(core4_swap(record, torev)); } // U_EMRINTERSECTCLIPRECT 30 -void U_EMRINTERSECTCLIPRECT_swap(char *record, int torev){ - core4_swap(record, torev); +int U_EMRINTERSECTCLIPRECT_swap(char *record, int torev){ + return(core4_swap(record, torev)); } // U_EMRSCALEVIEWPORTEXTEX 31 -void U_EMRSCALEVIEWPORTEXTEX_swap(char *record, int torev){ - core4_swap(record, torev); +int U_EMRSCALEVIEWPORTEXTEX_swap(char *record, int torev){ + return(core4_swap(record, torev)); } - // U_EMRSCALEWINDOWEXTEX 32 -void U_EMRSCALEWINDOWEXTEX_swap(char *record, int torev){ - core4_swap(record, torev); +int U_EMRSCALEWINDOWEXTEX_swap(char *record, int torev){ + return(core4_swap(record, torev)); } // U_EMRSAVEDC 33 -void U_EMRSAVEDC_swap(char *record, int torev){ - core5_swap(record, torev); +int U_EMRSAVEDC_swap(char *record, int torev){ + return(core5_swap(record, torev)); } // U_EMRRESTOREDC 34 -void U_EMRRESTOREDC_swap(char *record, int torev){ - core3_swap(record, torev); +int U_EMRRESTOREDC_swap(char *record, int torev){ + return(core3_swap(record, torev)); } // U_EMRSETWORLDTRANSFORM 35 -void U_EMRSETWORLDTRANSFORM_swap(char *record, int torev){ - core5_swap(record, torev); +int U_EMRSETWORLDTRANSFORM_swap(char *record, int torev){ + if(!core5_swap(record, torev))return(0); PU_EMRSETWORLDTRANSFORM pEmr = (PU_EMRSETWORLDTRANSFORM)(record); xform_swap(&(pEmr->xform)); + return(1); } // U_EMRMODIFYWORLDTRANSFORM 36 -void U_EMRMODIFYWORLDTRANSFORM_swap(char *record, int torev){ - core5_swap(record, torev); +int U_EMRMODIFYWORLDTRANSFORM_swap(char *record, int torev){ + if(!core5_swap(record, torev))return(0); PU_EMRMODIFYWORLDTRANSFORM pEmr = (PU_EMRMODIFYWORLDTRANSFORM)(record); xform_swap(&(pEmr->xform)); // xform U_swap4(&(pEmr->iMode),1); // iMode + return(1); } // U_EMRSELECTOBJECT 37 -void U_EMRSELECTOBJECT_swap(char *record, int torev){ - core5_swap(record, torev); +int U_EMRSELECTOBJECT_swap(char *record, int torev){ + if(!core5_swap(record, torev))return(0); PU_EMRSELECTOBJECT pEmr = (PU_EMRSELECTOBJECT)(record); U_swap4(&(pEmr->ihObject),1); // ihObject + return(1); } // U_EMRCREATEPEN 38 -void U_EMRCREATEPEN_swap(char *record, int torev){ - core5_swap(record, torev); +int U_EMRCREATEPEN_swap(char *record, int torev){ + if(!core5_swap(record, torev))return(0); PU_EMRCREATEPEN pEmr = (PU_EMRCREATEPEN)(record); U_swap4(&(pEmr->ihPen),1); // ihPen logpen_swap(&(pEmr->lopn)); // lopn + return(1); } // U_EMRCREATEBRUSHINDIRECT 39 -void U_EMRCREATEBRUSHINDIRECT_swap(char *record, int torev){ - core5_swap(record, torev); +int U_EMRCREATEBRUSHINDIRECT_swap(char *record, int torev){ + if(!core5_swap(record, torev))return(0); PU_EMRCREATEBRUSHINDIRECT pEmr = (PU_EMRCREATEBRUSHINDIRECT)(record); U_swap4(&(pEmr->ihBrush),1); // ihBrush logbrush_swap(&(pEmr->lb)); // lb + return(1); } // U_EMRDELETEOBJECT 40 -void U_EMRDELETEOBJECT_swap(char *record, int torev){ - core5_swap(record, torev); +int U_EMRDELETEOBJECT_swap(char *record, int torev){ + if(!core5_swap(record, torev))return(0); PU_EMRDELETEOBJECT pEmr = (PU_EMRDELETEOBJECT)(record); U_swap4(&(pEmr->ihObject),1); // ihObject + return(1); } // U_EMRANGLEARC 41 -void U_EMRANGLEARC_swap(char *record, int torev){ - core5_swap(record, torev); +int U_EMRANGLEARC_swap(char *record, int torev){ + if(!core5_swap(record, torev))return(0); PU_EMRANGLEARC pEmr = (PU_EMRANGLEARC)(record); pointl_swap(&(pEmr->ptlCenter),1); // ptlCenter U_swap4(&(pEmr->nRadius),3); // nRadius eStartAngle eSweepAngle + return(1); } // U_EMRELLIPSE 42 -void U_EMRELLIPSE_swap(char *record, int torev){ - core4_swap(record, torev); +int U_EMRELLIPSE_swap(char *record, int torev){ + return(core4_swap(record, torev)); } // U_EMRRECTANGLE 43 -void U_EMRRECTANGLE_swap(char *record, int torev){ - core4_swap(record, torev); +int U_EMRRECTANGLE_swap(char *record, int torev){ + return(core4_swap(record, torev)); } // U_EMRROUNDRECT 44 -void U_EMRROUNDRECT_swap(char *record, int torev){ - core5_swap(record, torev); +int U_EMRROUNDRECT_swap(char *record, int torev){ + if(!core5_swap(record, torev))return(0); PU_EMRROUNDRECT pEmr = (PU_EMRROUNDRECT)(record); rectl_swap(&(pEmr->rclBox),1); // rclBox sizel_swap(&(pEmr->szlCorner), 1); // szlCorner + return(1); } // U_EMRARC 45 -void U_EMRARC_swap(char *record, int torev){ - core9_swap(record, torev); +int U_EMRARC_swap(char *record, int torev){ + return(core9_swap(record, torev)); } // U_EMRCHORD 46 -void U_EMRCHORD_swap(char *record, int torev){ - core9_swap(record, torev); +int U_EMRCHORD_swap(char *record, int torev){ + return(core9_swap(record, torev)); } // U_EMRPIE 47 -void U_EMRPIE_swap(char *record, int torev){ - core9_swap(record, torev); +int U_EMRPIE_swap(char *record, int torev){ + return(core9_swap(record, torev)); } // U_EMRSELECTPALETTE 48 -void U_EMRSELECTPALETTE_swap(char *record, int torev){ - core3_swap(record, torev); +int U_EMRSELECTPALETTE_swap(char *record, int torev){ + return(core3_swap(record, torev)); } // U_EMRCREATEPALETTE 49 -void U_EMRCREATEPALETTE_swap(char *record, int torev){ - core5_swap(record, torev); +int U_EMRCREATEPALETTE_swap(char *record, int torev){ + if(!core5_swap(record, torev))return(0); PU_EMRCREATEPALETTE pEmr = (PU_EMRCREATEPALETTE)(record); U_swap4(&(pEmr->ihPal),1); // ihPal logpalette_swap( (PU_LOGPALETTE)&(pEmr->lgpl) ); // lgpl + return(1); } // U_EMRSETPALETTEENTRIES 50 -void U_EMRSETPALETTEENTRIES_swap(char *record, int torev){ - core5_swap(record, torev); +int U_EMRSETPALETTEENTRIES_swap(char *record, int torev){ + if(!core5_swap(record, torev))return(0); PU_EMRSETPALETTEENTRIES pEmr = (PU_EMRSETPALETTEENTRIES)(record); U_swap4(&(pEmr->ihPal),3); // ihPal iStart cEntries // ordered bytes: aPalEntries[] + return(1); } // U_EMRRESIZEPALETTE 51 -void U_EMRRESIZEPALETTE_swap(char *record, int torev){ - core7_swap(record, torev); +int U_EMRRESIZEPALETTE_swap(char *record, int torev){ + return(core7_swap(record, torev)); } // U_EMRREALIZEPALETTE 52 -void U_EMRREALIZEPALETTE_swap(char *record, int torev){ - core5_swap(record, torev); +int U_EMRREALIZEPALETTE_swap(char *record, int torev){ + return(core5_swap(record, torev)); } // U_EMREXTFLOODFILL 53 -void U_EMREXTFLOODFILL_swap(char *record, int torev){ - core5_swap(record, torev); +int U_EMREXTFLOODFILL_swap(char *record, int torev){ + if(!core5_swap(record, torev))return(0); PU_EMREXTFLOODFILL pEmr = (PU_EMREXTFLOODFILL)(record); pointl_swap(&(pEmr->ptlStart),1); // ptlStart // ordered bytes: crColor U_swap4(&(pEmr->iMode),1); // iMode + return(1); } // U_EMRLINETO 54 -void U_EMRLINETO_swap(char *record, int torev){ - core7_swap(record, torev); +int U_EMRLINETO_swap(char *record, int torev){ + return(core7_swap(record, torev)); } // U_EMRARCTO 55 -void U_EMRARCTO_swap(char *record, int torev){ - core9_swap(record, torev); +int U_EMRARCTO_swap(char *record, int torev){ + return(core9_swap(record, torev)); } // U_EMRPOLYDRAW 56 -void U_EMRPOLYDRAW_swap(char *record, int torev){ +int U_EMRPOLYDRAW_swap(char *record, int torev){ int count=0; - core5_swap(record, torev); + const char *blimit = NULL; PU_EMRPOLYDRAW pEmr = (PU_EMRPOLYDRAW)(record); if(torev){ count = pEmr->cptl; + blimit = record + pEmr->emr.nSize; } + if(!core5_swap(record, torev))return(0); rectl_swap(&(pEmr->rclBounds),1); // rclBounds U_swap4(&(pEmr->cptl),1); // cptl if(!torev){ count = pEmr->cptl; + blimit = record + pEmr->emr.nSize; } + if(IS_MEM_UNSAFE((pEmr->aptl), count*sizeof(U_POINTL), blimit))return(0); pointl_swap(pEmr->aptl,count); // aptl[] - U_swap4(pEmr->abTypes,count); // abTypes[] + // single byte data abTypes + return(1); } // U_EMRSETARCDIRECTION 57 -void U_EMRSETARCDIRECTION_swap(char *record, int torev){ - core3_swap(record, torev); +int U_EMRSETARCDIRECTION_swap(char *record, int torev){ + return(core3_swap(record, torev)); } // U_EMRSETMITERLIMIT 58 -void U_EMRSETMITERLIMIT_swap(char *record, int torev){ - core3_swap(record, torev); +int U_EMRSETMITERLIMIT_swap(char *record, int torev){ + return(core3_swap(record, torev)); } // U_EMRBEGINPATH 59 -void U_EMRBEGINPATH_swap(char *record, int torev){ - core5_swap(record, torev); +int U_EMRBEGINPATH_swap(char *record, int torev){ + return(core5_swap(record, torev)); } // U_EMRENDPATH 60 -void U_EMRENDPATH_swap(char *record, int torev){ - core5_swap(record, torev); +int U_EMRENDPATH_swap(char *record, int torev){ + return(core5_swap(record, torev)); } // U_EMRCLOSEFIGURE 61 -void U_EMRCLOSEFIGURE_swap(char *record, int torev){ - core5_swap(record, torev); +int U_EMRCLOSEFIGURE_swap(char *record, int torev){ + return(core5_swap(record, torev)); } // U_EMRFILLPATH 62 -void U_EMRFILLPATH_swap(char *record, int torev){ - core4_swap(record, torev); +int U_EMRFILLPATH_swap(char *record, int torev){ + return(core4_swap(record, torev)); } // U_EMRSTROKEANDFILLPATH 63 -void U_EMRSTROKEANDFILLPATH_swap(char *record, int torev){ - core4_swap(record, torev); +int U_EMRSTROKEANDFILLPATH_swap(char *record, int torev){ + return(core4_swap(record, torev)); } // U_EMRSTROKEPATH 64 -void U_EMRSTROKEPATH_swap(char *record, int torev){ - core4_swap(record, torev); +int U_EMRSTROKEPATH_swap(char *record, int torev){ + return(core4_swap(record, torev)); } // U_EMRFLATTENPATH 65 -void U_EMRFLATTENPATH_swap(char *record, int torev){ - core5_swap(record, torev); +int U_EMRFLATTENPATH_swap(char *record, int torev){ + return(core5_swap(record, torev)); } // U_EMRWIDENPATH 66 -void U_EMRWIDENPATH_swap(char *record, int torev){ - core5_swap(record, torev); +int U_EMRWIDENPATH_swap(char *record, int torev){ + return(core5_swap(record, torev)); } // U_EMRSELECTCLIPPATH 67 -void U_EMRSELECTCLIPPATH_swap(char *record, int torev){ - core3_swap(record, torev); +int U_EMRSELECTCLIPPATH_swap(char *record, int torev){ + return(core3_swap(record, torev)); } // U_EMRABORTPATH 68 -void U_EMRABORTPATH_swap(char *record, int torev){ - core5_swap(record, torev); +int U_EMRABORTPATH_swap(char *record, int torev){ + return(core5_swap(record, torev)); } // U_EMRUNDEF69 69 #define U_EMRUNDEF69_swap(A,B) U_EMRNOTIMPLEMENTED_swap(A,B) //!< Not implemented. // U_EMRCOMMENT 70 Comment (any binary data, interpretation is program specific) -void U_EMRCOMMENT_swap(char *record, int torev){ - core5_swap(record, torev); +int U_EMRCOMMENT_swap(char *record, int torev){ + int cbData = 0; + const char *blimit = NULL; PU_EMRCOMMENT pEmr = (PU_EMRCOMMENT)(record); + if(torev){ + cbData = pEmr->cbData; + blimit = record + pEmr->emr.nSize; + } + if(!core5_swap(record, torev))return(0); U_swap4(&(pEmr->cbData),1); // cbData + if(!torev){ + cbData = pEmr->cbData; + blimit = record + pEmr->emr.nSize; + } + if(IS_MEM_UNSAFE(record, cbData + sizeof(U_SIZE_EMRCOMMENT), blimit))return(0); // program specific data, presumably byte ordered, otherwise, not portable + return(1); } // U_EMRFILLRGN 71 -void U_EMRFILLRGN_swap(char *record, int torev){ - int roff=0; - int nextroff=0; - int limit=0; - roff=0; +int U_EMRFILLRGN_swap(char *record, int torev){ + int cbRgnData=0; + const char *blimit = NULL; PU_EMRFILLRGN pEmr = (PU_EMRFILLRGN)(record); if(torev){ - limit = pEmr->emr.nSize; - nextroff = 0; - } - core5_swap(record, torev); - if(!torev){ - limit = pEmr->emr.nSize; + cbRgnData= pEmr->cbRgnData; + blimit = record + pEmr->emr.nSize; } + if(!core5_swap(record, torev))return(0); rectl_swap(&(pEmr->rclBounds),1); // rclBounds U_swap4(&(pEmr->cbRgnData),2); // cbRgnData ihBrush - // This one is a pain since each RGNDATA may be a different size, so it isn't possible to index through them. - char *prd = (char *) &(pEmr->RgnData); - while(roff + 28 < limit){ // up to the end of the record - if(torev){ - nextroff += (((PU_RGNDATA)prd)->rdh.dwSize + ((PU_RGNDATA)prd)->rdh.nRgnSize - 16); - rgndata_swap((PU_RGNDATA) (prd + roff)); - roff = nextroff; - } - else { - rgndata_swap((PU_RGNDATA) (prd + roff)); - roff += (((PU_RGNDATA)prd)->rdh.dwSize + ((PU_RGNDATA)prd)->rdh.nRgnSize - 16); - } + if(!torev){ + cbRgnData= pEmr->cbRgnData; + blimit = record + pEmr->emr.nSize; } + if(IS_MEM_UNSAFE(pEmr->RgnData, cbRgnData, blimit))return(0); + return(rgndata_swap(pEmr->RgnData, cbRgnData, torev)); } // U_EMRFRAMERGN 72 -void U_EMRFRAMERGN_swap(char *record, int torev){ - int roff=0; - int nextroff=0; - int limit=0; +int U_EMRFRAMERGN_swap(char *record, int torev){ + int cbRgnData=0; + const char *blimit = NULL; PU_EMRFRAMERGN pEmr = (PU_EMRFRAMERGN)(record); - roff = 0; if(torev){ - limit = pEmr->emr.nSize; - nextroff = 0; - } - core5_swap(record, torev); - if(!torev){ - limit = pEmr->emr.nSize; + cbRgnData= pEmr->cbRgnData; + blimit = record + pEmr->emr.nSize; } + if(!core5_swap(record, torev))return(0); rectl_swap(&(pEmr->rclBounds),1); // rclBounds U_swap4(&(pEmr->cbRgnData),2); // cbRgnData ihBrush - sizel_swap(&(pEmr->szlStroke), 2); // szlStroke - // This one is a pain since each RGNDATA may be a different size, so it isn't possible to index through them. - char *prd = (char *) &(pEmr->RgnData); - while(roff + 28 < limit){ // up to the end of the record - if(torev){ - nextroff += (((PU_RGNDATA)prd)->rdh.dwSize + ((PU_RGNDATA)prd)->rdh.nRgnSize - 16); - rgndata_swap((PU_RGNDATA) (prd + roff)); - roff = nextroff; - } - else { - rgndata_swap((PU_RGNDATA) (prd + roff)); - roff += (((PU_RGNDATA)prd)->rdh.dwSize + ((PU_RGNDATA)prd)->rdh.nRgnSize - 16); - } + sizel_swap(&(pEmr->szlStroke), 1); // szlStroke + if(!torev){ + cbRgnData= pEmr->cbRgnData; + blimit = record + pEmr->emr.nSize; } + if(IS_MEM_UNSAFE(pEmr->RgnData, cbRgnData, blimit))return(0); + return(rgndata_swap(pEmr->RgnData, cbRgnData, torev)); } // U_EMRINVERTRGN 73 -void U_EMRINVERTRGN_swap(char *record, int torev){ - core11_swap(record, torev); +int U_EMRINVERTRGN_swap(char *record, int torev){ + return(core11_swap(record, torev)); } // U_EMRPAINTRGN 74 -void U_EMRPAINTRGN_swap(char *record, int torev){ - core11_swap(record, torev); +int U_EMRPAINTRGN_swap(char *record, int torev){ + return(core11_swap(record, torev)); } // U_EMREXTSELECTCLIPRGN 75 -void U_EMREXTSELECTCLIPRGN_swap(char *record, int torev){ - int roff=0; - int nextroff=0; - int limit=0; - PU_EMREXTSELECTCLIPRGN pEmr = (PU_EMREXTSELECTCLIPRGN) (record); +int U_EMREXTSELECTCLIPRGN_swap(char *record, int torev){ + int cbRgnData=0; + const char *blimit = NULL; + PU_EMREXTSELECTCLIPRGN pEmr = (PU_EMREXTSELECTCLIPRGN)(record); if(torev){ - limit = pEmr->emr.nSize; - } - core5_swap(record, torev); - if(!torev){ - limit = pEmr->emr.nSize; + cbRgnData= pEmr->cbRgnData; + blimit = record + pEmr->emr.nSize; } + if(!core5_swap(record, torev))return(0); U_swap4(&(pEmr->cbRgnData),2); // cbRgnData iMode - // This one is a pain since each RGNDATA may be a different size, so it isn't possible to index through them. - char *prd = (char *) &(pEmr->RgnData); - nextroff = roff = 0; - while(roff + 16 < limit){ // up to the end of the record - if(torev){ - nextroff += (((PU_RGNDATA)prd)->rdh.dwSize + ((PU_RGNDATA)prd)->rdh.nRgnSize - 16); - rgndata_swap((PU_RGNDATA) (prd + roff)); - roff = nextroff; - } - else { - rgndata_swap((PU_RGNDATA) (prd + roff)); - roff += (((PU_RGNDATA)prd)->rdh.dwSize + ((PU_RGNDATA)prd)->rdh.nRgnSize - 16); - } + if(!torev){ + cbRgnData= pEmr->cbRgnData; + blimit = record + pEmr->emr.nSize; } + if(IS_MEM_UNSAFE(pEmr->RgnData, cbRgnData, blimit))return(0); + return(rgndata_swap(pEmr->RgnData, cbRgnData, torev)); } // U_EMRBITBLT 76 -void U_EMRBITBLT_swap(char *record, int torev){ - core5_swap(record, torev); +int U_EMRBITBLT_swap(char *record, int torev){ + const char *blimit = NULL; + U_OFFBMISRC offBmiSrc = 0; + U_CBBMISRC cbBmiSrc = 0; + U_OFFBITSSRC offBitsSrc = 0; + U_CBBITSSRC cbBitsSrc = 0; + uint32_t iUsageSrc = 0; PU_EMRBITBLT pEmr = (PU_EMRBITBLT) (record); - if(torev && pEmr->cbBmiSrc){ - bitmapinfo_swap((PU_BITMAPINFO)(record + pEmr->offBmiSrc)); - } + if(torev){ + offBmiSrc = pEmr->offBmiSrc; + cbBmiSrc = pEmr->cbBmiSrc; + offBitsSrc = pEmr->offBitsSrc; + cbBitsSrc = pEmr->cbBitsSrc; + iUsageSrc = pEmr->iUsageSrc; + blimit = record + pEmr->emr.nSize; + if(!DIB_swap(record, iUsageSrc, offBmiSrc, cbBmiSrc, offBitsSrc, cbBitsSrc, blimit, torev))return(0); + } + if(!core5_swap(record, torev))return(0); rectl_swap(&(pEmr->rclBounds),1); // rclBounds pointl_swap(&(pEmr->Dest),2); // Dest cDest U_swap4(&(pEmr->dwRop),1); // dwRop @@ -1226,42 +1389,89 @@ void U_EMRBITBLT_swap(char *record, int torev){ xform_swap(&(pEmr->xformSrc)); // xformSrc // ordered bytes: crBkColorSrc U_swap4(&(pEmr->iUsageSrc),5); // iUsageSrc offBmiSrc cbBmiSrc offBitsSrc cbBitsSrc - // ordered bytes: bitmap (including 16 bit 5bit/channel color mode, which is done bytewise). - if(!torev && pEmr->cbBmiSrc){ - bitmapinfo_swap((PU_BITMAPINFO)(record + pEmr->offBmiSrc)); + if(!torev){ + offBmiSrc = pEmr->offBmiSrc; + cbBmiSrc = pEmr->cbBmiSrc; + offBitsSrc = pEmr->offBitsSrc; + cbBitsSrc = pEmr->cbBitsSrc; + iUsageSrc = pEmr->iUsageSrc; + blimit = record + pEmr->emr.nSize; + if(!DIB_swap(record, iUsageSrc, offBmiSrc, cbBmiSrc, offBitsSrc, cbBitsSrc, blimit, torev))return(0); } + // ordered bytes: bitmap (including 16 bit 5bit/channel color mode, which is done bytewise). + return(1); } // U_EMRSTRETCHBLT 77 -void U_EMRSTRETCHBLT_swap(char *record, int torev){ +int U_EMRSTRETCHBLT_swap(char *record, int torev){ + const char *blimit = NULL; + U_OFFBMISRC offBmiSrc = 0; + U_CBBMISRC cbBmiSrc = 0; + U_OFFBITSSRC offBitsSrc = 0; + U_CBBITSSRC cbBitsSrc = 0; + uint32_t iUsageSrc = 0; PU_EMRSTRETCHBLT pEmr = (PU_EMRSTRETCHBLT) (record); - core5_swap(record, torev); + if(torev){ + offBmiSrc = pEmr->offBmiSrc; + cbBmiSrc = pEmr->cbBmiSrc; + offBitsSrc = pEmr->offBitsSrc; + cbBitsSrc = pEmr->cbBitsSrc; + iUsageSrc = pEmr->iUsageSrc; + blimit = record + pEmr->emr.nSize; + if(!DIB_swap(record, iUsageSrc, offBmiSrc, cbBmiSrc, offBitsSrc, cbBitsSrc, blimit, torev))return(0); + } + if(!core5_swap(record, torev))return(0); rectl_swap(&(pEmr->rclBounds),1); // rclBounds pointl_swap(&(pEmr->Dest),2); // Dest cDest U_swap4(&(pEmr->dwRop),1); // dwRop pointl_swap(&(pEmr->Src),1); // Src xform_swap(&(pEmr->xformSrc)); // xformSrc // ordered bytes: crBkColorSrc - if(torev && pEmr->cbBmiSrc){ - bitmapinfo_swap((PU_BITMAPINFO)(record + pEmr->offBmiSrc)); - } U_swap4(&(pEmr->iUsageSrc),5); // iUsageSrc offBmiSrc cbBmiSrc offBitsSrc cbBitsSrc pointl_swap(&(pEmr->cSrc),1); // cSrc - if(!torev && pEmr->cbBmiSrc){ - bitmapinfo_swap((PU_BITMAPINFO)(record + pEmr->offBmiSrc)); + if(!torev){ + offBmiSrc = pEmr->offBmiSrc; + cbBmiSrc = pEmr->cbBmiSrc; + offBitsSrc = pEmr->offBitsSrc; + cbBitsSrc = pEmr->cbBitsSrc; + iUsageSrc = pEmr->iUsageSrc; + blimit = record + pEmr->emr.nSize; + if(!DIB_swap(record, iUsageSrc, offBmiSrc, cbBmiSrc, offBitsSrc, cbBitsSrc, blimit, torev))return(0); } + // ordered bytes: bitmap (including 16 bit 5bit/channel color mode, which is done bytewise). + return(1); } // U_EMRMASKBLT 78 -void U_EMRMASKBLT_swap(char *record, int torev){ +int U_EMRMASKBLT_swap(char *record, int torev){ + const char *blimit = NULL; + U_OFFBMISRC offBmiSrc = 0; + U_CBBMISRC cbBmiSrc = 0; + U_OFFBITSSRC offBitsSrc = 0; + U_CBBITSSRC cbBitsSrc = 0; + uint32_t iUsageSrc = 0; + U_OFFBMIMSK offBmiMask = 0; + U_CBBMIMSK cbBmiMask = 0; + U_OFFBITSMSK offBitsMask = 0; + U_CBBITSMSK cbBitsMask = 0; + uint32_t iUsageMask = 0; PU_EMRMASKBLT pEmr = (PU_EMRMASKBLT) (record); - core5_swap(record, torev); - if(torev && pEmr->cbBmiSrc){ - bitmapinfo_swap((PU_BITMAPINFO)(record + pEmr->offBmiSrc)); - } - if(torev && pEmr->cbBmiMask){ - bitmapinfo_swap((PU_BITMAPINFO)(record + pEmr->offBmiMask)); - } + if(torev){ + offBmiSrc = pEmr->offBmiSrc; + cbBmiSrc = pEmr->cbBmiSrc; + offBitsSrc = pEmr->offBitsSrc; + cbBitsSrc = pEmr->cbBitsSrc; + iUsageSrc = pEmr->iUsageSrc; + offBmiMask = pEmr->offBmiMask; + cbBmiMask = pEmr->cbBmiMask; + offBitsMask = pEmr->offBitsMask; + cbBitsMask = pEmr->cbBitsMask; + iUsageMask = pEmr->iUsageMask; + blimit = record + pEmr->emr.nSize; + if(!DIB_swap(record, iUsageSrc, offBmiSrc, cbBmiSrc, offBitsSrc, cbBitsSrc, blimit, torev))return(0); + if(!DIB_swap(record, iUsageMask, offBmiMask, cbBmiMask, offBitsMask, cbBitsMask, blimit, torev))return(0); + } + if(!core5_swap(record, torev))return(0); rectl_swap(&(pEmr->rclBounds),1); // rclBounds pointl_swap(&(pEmr->Dest),2); // Dest cDest U_swap4(&(pEmr->dwRop),1); // dwRop @@ -1271,24 +1481,54 @@ void U_EMRMASKBLT_swap(char *record, int torev){ U_swap4(&(pEmr->iUsageSrc),5); // iUsageSrc offBmiSrc cbBmiSrc offBitsSrc cbBitsSrc pointl_swap(&(pEmr->Mask),1); // Mask U_swap4(&(pEmr->iUsageMask),5); // iUsageMask offBmiMask cbBmiMask offBitsMask cbBitsMask - if(!torev && pEmr->cbBmiSrc){ - bitmapinfo_swap((PU_BITMAPINFO)(record + pEmr->offBmiSrc)); - } - if(!torev && pEmr->cbBmiMask){ - bitmapinfo_swap((PU_BITMAPINFO)(record + pEmr->offBmiMask)); - } + if(!torev){ + offBmiSrc = pEmr->offBmiSrc; + cbBmiSrc = pEmr->cbBmiSrc; + offBitsSrc = pEmr->offBitsSrc; + cbBitsSrc = pEmr->cbBitsSrc; + iUsageSrc = pEmr->iUsageSrc; + offBmiMask = pEmr->offBmiMask; + cbBmiMask = pEmr->cbBmiMask; + offBitsMask = pEmr->offBitsMask; + cbBitsMask = pEmr->cbBitsMask; + iUsageMask = pEmr->iUsageMask; + blimit = record + pEmr->emr.nSize; + if(!DIB_swap(record, iUsageSrc, offBmiSrc, cbBmiSrc, offBitsSrc, cbBitsSrc, blimit, torev))return(0); + if(!DIB_swap(record, iUsageMask, offBmiMask, cbBmiMask, offBitsMask, cbBitsMask, blimit, torev))return(0); + } + return(1); } // U_EMRPLGBLT 79 -void U_EMRPLGBLT_swap(char *record, int torev){ +int U_EMRPLGBLT_swap(char *record, int torev){ + const char *blimit = NULL; + U_OFFBMISRC offBmiSrc = 0; + U_CBBMISRC cbBmiSrc = 0; + U_OFFBITSSRC offBitsSrc = 0; + U_CBBITSSRC cbBitsSrc = 0; + uint32_t iUsageSrc = 0; + U_OFFBMIMSK offBmiMask = 0; + U_CBBMIMSK cbBmiMask = 0; + U_OFFBITSMSK offBitsMask = 0; + U_CBBITSMSK cbBitsMask = 0; + uint32_t iUsageMask = 0; PU_EMRPLGBLT pEmr = (PU_EMRPLGBLT) (record); - core5_swap(record, torev); - if(torev && pEmr->cbBmiSrc){ - bitmapinfo_swap((PU_BITMAPINFO)(record + pEmr->offBmiSrc)); - } - if(torev && pEmr->cbBmiMask){ - bitmapinfo_swap((PU_BITMAPINFO)(record + pEmr->offBmiMask)); - } + if(torev){ + offBmiSrc = pEmr->offBmiSrc; + cbBmiSrc = pEmr->cbBmiSrc; + offBitsSrc = pEmr->offBitsSrc; + cbBitsSrc = pEmr->cbBitsSrc; + iUsageSrc = pEmr->iUsageSrc; + offBmiMask = pEmr->offBmiMask; + cbBmiMask = pEmr->cbBmiMask; + offBitsMask = pEmr->offBitsMask; + cbBitsMask = pEmr->cbBitsMask; + iUsageMask = pEmr->iUsageMask; + blimit = record + pEmr->emr.nSize; + if(!DIB_swap(record, iUsageSrc, offBmiSrc, cbBmiSrc, offBitsSrc, cbBitsSrc, blimit, torev))return(0); + if(!DIB_swap(record, iUsageMask, offBmiMask, cbBmiMask, offBitsMask, cbBitsMask, blimit, torev))return(0); + } + if(!core5_swap(record, torev))return(0); rectl_swap(&(pEmr->rclBounds),1); // rclBounds pointl_swap(pEmr->aptlDst,3); // aptlDst[] pointl_swap(&(pEmr->Src),2); // Src cSrc @@ -1297,78 +1537,124 @@ void U_EMRPLGBLT_swap(char *record, int torev){ U_swap4(&(pEmr->iUsageSrc),5); // iUsageSrc offBmiSrc cbBmiSrc offBitsSrc cbBitsSrc pointl_swap(&(pEmr->Mask),1); // Mask U_swap4(&(pEmr->iUsageMask),5); // iUsageMask offBmiMask cbBmiMask offBitsMask cbBitsMask - if(!torev && pEmr->cbBmiSrc){ - bitmapinfo_swap((PU_BITMAPINFO)(record + pEmr->offBmiSrc)); - } - if(!torev && pEmr->cbBmiMask){ - bitmapinfo_swap((PU_BITMAPINFO)(record + pEmr->offBmiMask)); - } + if(!torev){ + offBmiSrc = pEmr->offBmiSrc; + cbBmiSrc = pEmr->cbBmiSrc; + offBitsSrc = pEmr->offBitsSrc; + cbBitsSrc = pEmr->cbBitsSrc; + iUsageSrc = pEmr->iUsageSrc; + offBmiMask = pEmr->offBmiMask; + cbBmiMask = pEmr->cbBmiMask; + offBitsMask = pEmr->offBitsMask; + cbBitsMask = pEmr->cbBitsMask; + iUsageMask = pEmr->iUsageMask; + blimit = record + pEmr->emr.nSize; + if(!DIB_swap(record, iUsageSrc, offBmiSrc, cbBmiSrc, offBitsSrc, cbBitsSrc, blimit, torev))return(0); + if(!DIB_swap(record, iUsageMask, offBmiMask, cbBmiMask, offBitsMask, cbBitsMask, blimit, torev))return(0); + } + return(1); } // U_EMRSETDIBITSTODEVICE 80 -void U_EMRSETDIBITSTODEVICE_swap(char *record, int torev){ +int U_EMRSETDIBITSTODEVICE_swap(char *record, int torev){ + const char *blimit = NULL; + U_OFFBMISRC offBmiSrc = 0; + U_CBBMISRC cbBmiSrc = 0; + U_OFFBITSSRC offBitsSrc = 0; + U_CBBITSSRC cbBitsSrc = 0; + uint32_t iUsageSrc = 0; PU_EMRSETDIBITSTODEVICE pEmr = (PU_EMRSETDIBITSTODEVICE) (record); - core5_swap(record, torev); - if(torev && pEmr->cbBmiSrc){ - bitmapinfo_swap((PU_BITMAPINFO)(record + pEmr->offBmiSrc)); - } + if(torev){ + offBmiSrc = pEmr->offBmiSrc; + cbBmiSrc = pEmr->cbBmiSrc; + offBitsSrc = pEmr->offBitsSrc; + cbBitsSrc = pEmr->cbBitsSrc; + iUsageSrc = pEmr->iUsageSrc; + blimit = record + pEmr->emr.nSize; + if(!DIB_swap(record, iUsageSrc, offBmiSrc, cbBmiSrc, offBitsSrc, cbBitsSrc, blimit, torev))return(0); + } + if(!core5_swap(record, torev))return(0); rectl_swap(&(pEmr->rclBounds),1); // rclBounds pointl_swap(&(pEmr->Dest),1); // Dest pointl_swap(&(pEmr->Src),2); // Src cSrc U_swap4(&(pEmr->offBmiSrc),7); // offBmiSrc cbBmiSrc offBitsSrc cbBitsSrc iUsageSrc iStartScan cScans - if(!torev && pEmr->cbBmiSrc){ - bitmapinfo_swap((PU_BITMAPINFO)(record + pEmr->offBmiSrc)); + if(!torev){ + offBmiSrc = pEmr->offBmiSrc; + cbBmiSrc = pEmr->cbBmiSrc; + offBitsSrc = pEmr->offBitsSrc; + cbBitsSrc = pEmr->cbBitsSrc; + iUsageSrc = pEmr->iUsageSrc; + blimit = record + pEmr->emr.nSize; + if(!DIB_swap(record, iUsageSrc, offBmiSrc, cbBmiSrc, offBitsSrc, cbBitsSrc, blimit, torev))return(0); } + return(1); } // U_EMRSTRETCHDIBITS 81 -void U_EMRSTRETCHDIBITS_swap(char *record, int torev){ +int U_EMRSTRETCHDIBITS_swap(char *record, int torev){ + const char *blimit = NULL; + U_OFFBMISRC offBmiSrc = 0; + U_CBBMISRC cbBmiSrc = 0; + U_OFFBITSSRC offBitsSrc = 0; + U_CBBITSSRC cbBitsSrc = 0; + uint32_t iUsageSrc = 0; PU_EMRSTRETCHDIBITS pEmr = (PU_EMRSTRETCHDIBITS) (record); - core5_swap(record, torev); - if(torev && pEmr->cbBmiSrc){ - bitmapinfo_swap((PU_BITMAPINFO)(record + pEmr->offBmiSrc)); - } + if(torev){ + offBmiSrc = pEmr->offBmiSrc; + cbBmiSrc = pEmr->cbBmiSrc; + offBitsSrc = pEmr->offBitsSrc; + cbBitsSrc = pEmr->cbBitsSrc; + iUsageSrc = pEmr->iUsageSrc; + blimit = record + pEmr->emr.nSize; + if(!DIB_swap(record, iUsageSrc, offBmiSrc, cbBmiSrc, offBitsSrc, cbBitsSrc, blimit, torev))return(0); + } + if(!core5_swap(record, torev))return(0); rectl_swap(&(pEmr->rclBounds),1); // rclBounds pointl_swap(&(pEmr->Dest),1); // Dest pointl_swap(&(pEmr->Src),2); // Src cSrc U_swap4(&(pEmr->offBmiSrc),6); // offBmiSrc cbBmiSrc offBitsSrc cbBitsSrc iUsageSrc dwRop pointl_swap(&(pEmr->cDest),1); // cDest - if(!torev && pEmr->cbBmiSrc){ - bitmapinfo_swap((PU_BITMAPINFO)(record + pEmr->offBmiSrc)); + if(!torev){ + offBmiSrc = pEmr->offBmiSrc; + cbBmiSrc = pEmr->cbBmiSrc; + offBitsSrc = pEmr->offBitsSrc; + cbBitsSrc = pEmr->cbBitsSrc; + iUsageSrc = pEmr->iUsageSrc; + blimit = record + pEmr->emr.nSize; + if(!DIB_swap(record, iUsageSrc, offBmiSrc, cbBmiSrc, offBitsSrc, cbBitsSrc, blimit, torev))return(0); } + return(1); } -// U_EMREXTCREATEFONTINDIRECTW_swap 82 -void U_EMREXTCREATEFONTINDIRECTW_swap(char *record, int torev){ +// U_EMREXTCREATEFONTINDIRECTW 82 +int U_EMREXTCREATEFONTINDIRECTW_swap(char *record, int torev){ + int nSize = 0; PU_EMREXTCREATEFONTINDIRECTW pEmr = (PU_EMREXTCREATEFONTINDIRECTW) (record); if(torev){ - if(pEmr->emr.nSize == sizeof(U_EMREXTCREATEFONTINDIRECTW)){ - logfont_panose_swap(&(pEmr->elfw)); - } - else { - logfont_swap( (PU_LOGFONT) &(pEmr->elfw)); - } + nSize = pEmr->emr.nSize; } - core5_swap(record, torev); + if(!core5_swap(record, torev))return(0); + U_swap4(&(pEmr->ihFont),1); // ihFont if(!torev){ - if(pEmr->emr.nSize == sizeof(U_EMREXTCREATEFONTINDIRECTW)){ - logfont_panose_swap(&(pEmr->elfw)); - } - else { - logfont_swap( (PU_LOGFONT) &(pEmr->elfw)); - } + nSize = pEmr->emr.nSize; } - U_swap4(&(pEmr->ihFont),1); // ihFont + if(nSize == U_SIZE_EMREXTCREATEFONTINDIRECTW_LOGFONT_PANOSE){ + logfont_panose_swap(&(pEmr->elfw)); + } + else { // logfont or logfontExDv (which starts with logfont, which can be swapped, and the rest is already in byte order + logfont_swap( (PU_LOGFONT) &(pEmr->elfw)); + } + return(1); } // U_EMREXTTEXTOUTA 83 -void U_EMREXTTEXTOUTA_swap(char *record, int torev){ - core8_swap(record, torev); +int U_EMREXTTEXTOUTA_swap(char *record, int torev){ + return(core8_swap(record, torev)); } // U_EMREXTTEXTOUTW 84 -void U_EMREXTTEXTOUTW_swap(char *record, int torev){ - core8_swap(record, torev); +int U_EMREXTTEXTOUTW_swap(char *record, int torev){ + return(core8_swap(record, torev)); } // U_EMRPOLYBEZIER16 85 @@ -1376,23 +1662,23 @@ void U_EMREXTTEXTOUTW_swap(char *record, int torev){ \brief Convert a pointer to a U_EMR_POLYBEZIER16 record. \param record pointer to a buffer holding the EMR record */ -void U_EMRPOLYBEZIER16_swap(char *record, int torev){ - core6_swap(record, torev); +int U_EMRPOLYBEZIER16_swap(char *record, int torev){ + return(core6_swap(record, torev)); } // U_EMRPOLYGON16 86 -void U_EMRPOLYGON16_swap(char *record, int torev){ - core6_swap(record, torev); +int U_EMRPOLYGON16_swap(char *record, int torev){ + return(core6_swap(record, torev)); } // U_EMRPOLYLINE16 87 -void U_EMRPOLYLINE16_swap(char *record, int torev){ - core6_swap(record, torev); +int U_EMRPOLYLINE16_swap(char *record, int torev){ + return(core6_swap(record, torev)); } // U_EMRPOLYBEZIERTO16 88 -void U_EMRPOLYBEZIERTO16_swap(char *record, int torev){ - core6_swap(record, torev); +int U_EMRPOLYBEZIERTO16_swap(char *record, int torev){ + return(core6_swap(record, torev)); } // U_EMRPOLYLINETO16 89 @@ -1400,61 +1686,81 @@ void U_EMRPOLYBEZIERTO16_swap(char *record, int torev){ \brief Convert a pointer to a U_EMR_POLYLINETO16 record. \param record pointer to a buffer holding the EMR record */ -void U_EMRPOLYLINETO16_swap(char *record, int torev){ - core6_swap(record, torev); +int U_EMRPOLYLINETO16_swap(char *record, int torev){ + return(core6_swap(record, torev)); } // U_EMRPOLYPOLYLINE16 90 -void U_EMRPOLYPOLYLINE16_swap(char *record, int torev){ - core10_swap(record, torev); +int U_EMRPOLYPOLYLINE16_swap(char *record, int torev){ + return(core10_swap(record, torev)); } // U_EMRPOLYPOLYGON16 91 -void U_EMRPOLYPOLYGON16_swap(char *record, int torev){ - core10_swap(record, torev); +int U_EMRPOLYPOLYGON16_swap(char *record, int torev){ + return(core10_swap(record, torev)); } // U_EMRPOLYDRAW16 92 -void U_EMRPOLYDRAW16_swap(char *record, int torev){ +int U_EMRPOLYDRAW16_swap(char *record, int torev){ int count=0; - core5_swap(record, torev); + const char *blimit = NULL; PU_EMRPOLYDRAW16 pEmr = (PU_EMRPOLYDRAW16)(record); if(torev){ count = pEmr->cpts; + blimit = record + pEmr->emr.nSize; } + if(!core5_swap(record, torev))return(0); rectl_swap(&(pEmr->rclBounds),1); // rclBounds U_swap4(&(pEmr->cpts),1); // cpts if(!torev){ count = pEmr->cpts; + blimit = record + pEmr->emr.nSize; } + if(IS_MEM_UNSAFE((pEmr->apts), count*sizeof(U_POINT16), blimit))return(0); point16_swap(pEmr->apts,count); // apts[] - U_swap4(pEmr->abTypes,count); // abTypes[] + // single byte data abTypes + return(1); } // U_EMRCREATEMONOBRUSH 93 -void U_EMRCREATEMONOBRUSH_swap(char *record, int torev){ - core12_swap(record, torev); +int U_EMRCREATEMONOBRUSH_swap(char *record, int torev){ + return(core12_swap(record, torev)); } // U_EMRCREATEDIBPATTERNBRUSHPT_swap 94 -void U_EMRCREATEDIBPATTERNBRUSHPT_swap(char *record, int torev){ - core12_swap(record, torev); +int U_EMRCREATEDIBPATTERNBRUSHPT_swap(char *record, int torev){ + return(core12_swap(record, torev)); } // U_EMREXTCREATEPEN 95 -void U_EMREXTCREATEPEN_swap(char *record, int torev){ - core5_swap(record, torev); +int U_EMREXTCREATEPEN_swap(char *record, int torev){ + const char *blimit = NULL; + U_OFFBMI offBmi = 0; + U_CBBMI cbBmi = 0; + U_OFFBITS offBits = 0; + U_CBBITS cbBits = 0; PU_EMREXTCREATEPEN pEmr = (PU_EMREXTCREATEPEN)(record); - if(torev && pEmr->cbBmi){ - bitmapinfo_swap((PU_BITMAPINFO)(record + pEmr->offBmi)); - } + if(torev){ + offBmi = pEmr->offBmi; + cbBmi = pEmr->cbBmi; + offBits = pEmr->offBits; + cbBits = pEmr->cbBits; + blimit = record + pEmr->emr.nSize; + if(!DIB_swap(record, U_DIB_RGB_COLORS, offBmi, cbBmi, offBits, cbBits, blimit, torev))return(0); + } + if(!core5_swap(record, torev))return(0); U_swap4(&(pEmr->ihPen),5); // ihPen offBmi cbBmi offBits cbBits - if(!torev && pEmr->cbBmi){ - bitmapinfo_swap((PU_BITMAPINFO)(record + pEmr->offBmi)); + if(!torev){ + offBmi = pEmr->offBmi; + cbBmi = pEmr->cbBmi; + offBits = pEmr->offBits; + cbBits = pEmr->cbBits; + blimit = record + pEmr->emr.nSize; + if(!DIB_swap(record, U_DIB_RGB_COLORS, offBmi, cbBmi, offBits, cbBits, blimit, torev))return(0); } - extlogpen_swap((PU_EXTLOGPEN) &(pEmr->elp), torev); + return(extlogpen_swap((PU_EXTLOGPEN) &(pEmr->elp), blimit, torev)); } // U_EMRPOLYTEXTOUTA 96 NOT IMPLEMENTED, denigrated after Windows NT @@ -1463,26 +1769,27 @@ void U_EMREXTCREATEPEN_swap(char *record, int torev){ #define U_EMRPOLYTEXTOUTW_swap(A,B) U_EMRNOTIMPLEMENTED_swap(A,B) //!< Not implemented. // U_EMRSETICMMODE 98 -void U_EMRSETICMMODE_swap(char *record, int torev){ - core3_swap(record, torev); +int U_EMRSETICMMODE_swap(char *record, int torev){ + return(core3_swap(record, torev)); } // U_EMRCREATECOLORSPACE 99 -void U_EMRCREATECOLORSPACE_swap(char *record, int torev){ - core5_swap(record, torev); +int U_EMRCREATECOLORSPACE_swap(char *record, int torev){ + if(!core5_swap(record, torev))return(0); PU_EMRCREATECOLORSPACE pEmr = (PU_EMRCREATECOLORSPACE)(record); U_swap4(&(pEmr->ihCS),1); // ihCS logcolorspacea_swap(&(pEmr->lcs)); // lcs + return(1); } // U_EMRSETCOLORSPACE 100 -void U_EMRSETCOLORSPACE_swap(char *record, int torev){ - core3_swap(record, torev); +int U_EMRSETCOLORSPACE_swap(char *record, int torev){ + return(core3_swap(record, torev)); } // U_EMRDELETECOLORSPACE 101 -void U_EMRDELETECOLORSPACE_swap(char *record, int torev){ - core3_swap(record, torev); +int U_EMRDELETECOLORSPACE_swap(char *record, int torev){ + return(core3_swap(record, torev)); } // U_EMRGLSRECORD 102 Not implemented @@ -1491,10 +1798,11 @@ void U_EMRDELETECOLORSPACE_swap(char *record, int torev){ #define U_EMRGLSBOUNDEDRECORD_swap(A,B) U_EMRNOTIMPLEMENTED_swap(A,B) //!< Not implemented. // U_EMRPIXELFORMAT 104 -void U_EMRPIXELFORMAT_swap(char *record, int torev){ - core5_swap(record, torev); +int U_EMRPIXELFORMAT_swap(char *record, int torev){ + if(!core5_swap(record, torev))return(0); PU_EMRPIXELFORMAT pEmr = (PU_EMRPIXELFORMAT)(record); pixelformatdescriptor_swap(&(pEmr->pfd)); // pfd + return(1); } // U_EMRDRAWESCAPE 105 Not implemented @@ -1505,24 +1813,32 @@ void U_EMRPIXELFORMAT_swap(char *record, int torev){ #define U_EMRUNDEF107_swap(A,B) U_EMRNOTIMPLEMENTED_swap(A,B) //!< Not implemented. // U_EMRSMALLTEXTOUT 108 -void U_EMRSMALLTEXTOUT_swap(char *record, int torev){ - int roff=0; - int fuOptions=0; - core5_swap(record, torev); +int U_EMRSMALLTEXTOUT_swap(char *record, int torev){ + int roff=sizeof(U_EMRSMALLTEXTOUT); // offset to the start of the variable fields + int fuOptions = 0; + int cChars = 0; + const char *blimit = NULL; PU_EMRSMALLTEXTOUT pEmr = (PU_EMRSMALLTEXTOUT)(record); if(torev){ fuOptions = pEmr->fuOptions; + cChars = pEmr->cChars; + blimit = record + pEmr->emr.nSize; } + if(!core5_swap(record, torev))return(0); pointl_swap(&(pEmr->Dest),1); // Dest U_swap4(&(pEmr->cChars),5); // cChars fuOptions iGraphicsMode exScale eyScale if(!torev){ fuOptions = pEmr->fuOptions; + cChars = pEmr->cChars; + blimit = record + pEmr->emr.nSize; } - roff = sizeof(U_EMRSMALLTEXTOUT); // offset to the start of the variable fields if(!(fuOptions & U_ETO_NO_RECT)){ + if(IS_MEM_UNSAFE(record, roff + sizeof(U_RECTL), blimit))return(0); rectl_swap( (PU_RECTL) (record + roff),1); // rclBounds } + if(IS_MEM_UNSAFE(record, roff + sizeof(U_RECTL) + cChars, blimit))return(0); // ordered bytes or UTF16-LE TextString + return(1); } // U_EMRFORCEUFIMAPPING 109 Not implemented @@ -1537,55 +1853,62 @@ void U_EMRSMALLTEXTOUT_swap(char *record, int torev){ #define U_EMRSETICMPROFILEW_swap(A,B) U_EMRNOTIMPLEMENTED_swap(A,B) //!< Not implemented. // U_EMRALPHABLEND 114 -void U_EMRALPHABLEND_swap(char *record, int torev){ - core13_swap(record, torev); +int U_EMRALPHABLEND_swap(char *record, int torev){ + return(core13_swap(record, torev)); } // U_EMRSETLAYOUT 115 -void U_EMRSETLAYOUT_swap(char *record, int torev){ - core3_swap(record, torev); +int U_EMRSETLAYOUT_swap(char *record, int torev){ + return(core3_swap(record, torev)); } // U_EMRTRANSPARENTBLT 116 -void U_EMRTRANSPARENTBLT_swap(char *record, int torev){ - core13_swap(record, torev); +int U_EMRTRANSPARENTBLT_swap(char *record, int torev){ + return(core13_swap(record, torev)); } // U_EMRUNDEF117 117 Not implemented #define U_EMRUNDEF117_swap(A,B) U_EMRNOTIMPLEMENTED_swap(A,B) //!< Not implemented. // U_EMRGRADIENTFILL 118 -void U_EMRGRADIENTFILL_swap(char *record, int torev){ +int U_EMRGRADIENTFILL_swap(char *record, int torev){ int nTriVert=0; int nGradObj=0; int ulMode=0; - core5_swap(record, torev); + const char *blimit = NULL; PU_EMRGRADIENTFILL pEmr = (PU_EMRGRADIENTFILL)(record); if(torev){ nTriVert = pEmr->nTriVert; nGradObj = pEmr->nGradObj; ulMode = pEmr->ulMode; + blimit = record + pEmr->emr.nSize; } + if(!core5_swap(record, torev))return(0); rectl_swap(&(pEmr->rclBounds),1); // rclBounds U_swap4(&(pEmr->nTriVert),3); // nTriVert nGradObj ulMode if(!torev){ nTriVert = pEmr->nTriVert; nGradObj = pEmr->nGradObj; ulMode = pEmr->ulMode; + blimit = record + pEmr->emr.nSize; } record += sizeof(U_EMRGRADIENTFILL); + if(IS_MEM_UNSAFE(record, nTriVert*sizeof(U_TRIVERTEX), blimit))return(0); if(nTriVert){ trivertex_swap((PU_TRIVERTEX)(record),nTriVert); // TriVert[] } record += nTriVert * sizeof(U_TRIVERTEX); if(nGradObj){ if( ulMode == U_GRADIENT_FILL_TRIANGLE){ + if(IS_MEM_UNSAFE(record, nGradObj*sizeof(U_GRADIENT3), blimit))return(0); gradient3_swap((PU_GRADIENT3)(record), nGradObj); // GradObj[] } else if(ulMode == U_GRADIENT_FILL_RECT_H || ulMode == U_GRADIENT_FILL_RECT_V){ + if(IS_MEM_UNSAFE(record, nGradObj*sizeof(U_GRADIENT4), blimit))return(0); gradient4_swap((PU_GRADIENT4)(record), nGradObj); // GradObj[] } } + return(1); } // U_EMRSETLINKEDUFIS 119 Not implemented @@ -1596,17 +1919,185 @@ void U_EMRGRADIENTFILL_swap(char *record, int torev){ #define U_EMRCOLORMATCHTOTARGETW_swap(A,B) U_EMRNOTIMPLEMENTED_swap(A,B) //!< Not implemented. // U_EMRCREATECOLORSPACEW 122 -void U_EMRCREATECOLORSPACEW_swap(char *record, int torev){ - core5_swap(record, torev); +int U_EMRCREATECOLORSPACEW_swap(char *record, int torev){ PU_EMRCREATECOLORSPACEW pEmr = (PU_EMRCREATECOLORSPACEW)(record); + if(!core5_swap(record, torev))return(0); U_swap4(&(pEmr->ihCS),1); // ihCS logcolorspacew_swap(&(pEmr->lcs)); // lcs U_swap4(&(pEmr->dwFlags),2); // dwFlags cbData // ordered bytes: Data + return(1); } //! \endcond +/** + \brief Checks the declared size of a record for consistency + \return 0 on failure, 1 on success + \param record pointer to the start of the EMF record in memory + \param blimit pointer to one byte after the EMF record in memory + \param nSize number of bytes in the record, read from the record + \param iType type of the record, read from the record + \param torev 1 for native to reversed, 0 for reversed to native + + Normally this would be called immediately after reading the data from a file. + Verifies that the declared size is consistent with this type of record. +*/ +int U_emf_record_sizeok(const char *record, const char *blimit, uint32_t *nSize, uint32_t *iType, int torev){ + uint32_t rsize=0; + if(!nSize || !iType)return(0); // programming error + + /* Check that COMMON data in record can be touched without an access violation. If it cannot be + this is either a corrupt EMF or one engineered to cause a buffer overflow. Pointer math + could wrap so check both sides of the range, and fail any indications of such a wrap. + */ + if(IS_MEM_UNSAFE(record, sizeof(U_EMR), blimit))return(0); + + PU_ENHMETARECORD pEmr = (PU_ENHMETARECORD)(record); + *iType = pEmr->iType; + *nSize = pEmr->nSize; + if(!torev){ + U_swap4(iType,1); + U_swap4(nSize,1); + } + + /* Check that the FULL record size is OK, abort if not. */ + if(IS_MEM_UNSAFE(record, *nSize, blimit))return(0); + + switch (*iType) + { + // next line, ancient EMF files used a smaller header, to be safe, test for that + case U_EMR_HEADER: rsize = U_SIZE_EMRHEADER_MIN; break; + case U_EMR_POLYBEZIER: rsize = U_SIZE_EMRPOLYBEZIER; break; + case U_EMR_POLYGON: rsize = U_SIZE_EMRPOLYGON; break; + case U_EMR_POLYLINE: rsize = U_SIZE_EMRPOLYLINE; break; + case U_EMR_POLYBEZIERTO: rsize = U_SIZE_EMRPOLYBEZIERTO; break; + case U_EMR_POLYLINETO: rsize = U_SIZE_EMRPOLYLINETO; break; + case U_EMR_POLYPOLYLINE: rsize = U_SIZE_EMRPOLYPOLYLINE; break; + case U_EMR_POLYPOLYGON: rsize = U_SIZE_EMRPOLYPOLYGON; break; + case U_EMR_SETWINDOWEXTEX: rsize = U_SIZE_EMRSETWINDOWEXTEX; break; + case U_EMR_SETWINDOWORGEX: rsize = U_SIZE_EMRSETWINDOWORGEX; break; + case U_EMR_SETVIEWPORTEXTEX: rsize = U_SIZE_EMRSETVIEWPORTEXTEX; break; + case U_EMR_SETVIEWPORTORGEX: rsize = U_SIZE_EMRSETVIEWPORTORGEX; break; + case U_EMR_SETBRUSHORGEX: rsize = U_SIZE_EMRSETBRUSHORGEX; break; + case U_EMR_EOF: rsize = U_SIZE_EMREOF; break; + case U_EMR_SETPIXELV: rsize = U_SIZE_EMRSETPIXELV; break; + case U_EMR_SETMAPPERFLAGS: rsize = U_SIZE_EMRSETMAPPERFLAGS; break; + case U_EMR_SETMAPMODE: rsize = U_SIZE_EMRSETMAPMODE; break; + case U_EMR_SETBKMODE: rsize = U_SIZE_EMRSETBKMODE; break; + case U_EMR_SETPOLYFILLMODE: rsize = U_SIZE_EMRSETPOLYFILLMODE; break; + case U_EMR_SETROP2: rsize = U_SIZE_EMRSETROP2; break; + case U_EMR_SETSTRETCHBLTMODE: rsize = U_SIZE_EMRSETSTRETCHBLTMODE; break; + case U_EMR_SETTEXTALIGN: rsize = U_SIZE_EMRSETTEXTALIGN; break; + case U_EMR_SETCOLORADJUSTMENT: rsize = U_SIZE_EMRSETCOLORADJUSTMENT; break; + case U_EMR_SETTEXTCOLOR: rsize = U_SIZE_EMRSETTEXTCOLOR; break; + case U_EMR_SETBKCOLOR: rsize = U_SIZE_EMRSETBKCOLOR; break; + case U_EMR_OFFSETCLIPRGN: rsize = U_SIZE_EMROFFSETCLIPRGN; break; + case U_EMR_MOVETOEX: rsize = U_SIZE_EMRMOVETOEX; break; + case U_EMR_SETMETARGN: rsize = U_SIZE_EMRSETMETARGN; break; + case U_EMR_EXCLUDECLIPRECT: rsize = U_SIZE_EMREXCLUDECLIPRECT; break; + case U_EMR_INTERSECTCLIPRECT: rsize = U_SIZE_EMRINTERSECTCLIPRECT; break; + case U_EMR_SCALEVIEWPORTEXTEX: rsize = U_SIZE_EMRSCALEVIEWPORTEXTEX; break; + case U_EMR_SCALEWINDOWEXTEX: rsize = U_SIZE_EMRSCALEWINDOWEXTEX; break; + case U_EMR_SAVEDC: rsize = U_SIZE_EMRSAVEDC; break; + case U_EMR_RESTOREDC: rsize = U_SIZE_EMRRESTOREDC; break; + case U_EMR_SETWORLDTRANSFORM: rsize = U_SIZE_EMRSETWORLDTRANSFORM; break; + case U_EMR_MODIFYWORLDTRANSFORM: rsize = U_SIZE_EMRMODIFYWORLDTRANSFORM; break; + case U_EMR_SELECTOBJECT: rsize = U_SIZE_EMRSELECTOBJECT; break; + case U_EMR_CREATEPEN: rsize = U_SIZE_EMRCREATEPEN; break; + case U_EMR_CREATEBRUSHINDIRECT: rsize = U_SIZE_EMRCREATEBRUSHINDIRECT; break; + case U_EMR_DELETEOBJECT: rsize = U_SIZE_EMRDELETEOBJECT; break; + case U_EMR_ANGLEARC: rsize = U_SIZE_EMRANGLEARC; break; + case U_EMR_ELLIPSE: rsize = U_SIZE_EMRELLIPSE; break; + case U_EMR_RECTANGLE: rsize = U_SIZE_EMRRECTANGLE; break; + case U_EMR_ROUNDRECT: rsize = U_SIZE_EMRROUNDRECT; break; + case U_EMR_ARC: rsize = U_SIZE_EMRARC; break; + case U_EMR_CHORD: rsize = U_SIZE_EMRCHORD; break; + case U_EMR_PIE: rsize = U_SIZE_EMRPIE; break; + case U_EMR_SELECTPALETTE: rsize = U_SIZE_EMRSELECTPALETTE; break; + case U_EMR_CREATEPALETTE: rsize = U_SIZE_EMRCREATEPALETTE; break; + case U_EMR_SETPALETTEENTRIES: rsize = U_SIZE_EMRSETPALETTEENTRIES; break; + case U_EMR_RESIZEPALETTE: rsize = U_SIZE_EMRRESIZEPALETTE; break; + case U_EMR_REALIZEPALETTE: rsize = U_SIZE_EMRREALIZEPALETTE; break; + case U_EMR_EXTFLOODFILL: rsize = U_SIZE_EMREXTFLOODFILL; break; + case U_EMR_LINETO: rsize = U_SIZE_EMRLINETO; break; + case U_EMR_ARCTO: rsize = U_SIZE_EMRARCTO; break; + case U_EMR_POLYDRAW: rsize = U_SIZE_EMRPOLYDRAW; break; + case U_EMR_SETARCDIRECTION: rsize = U_SIZE_EMRSETARCDIRECTION; break; + case U_EMR_SETMITERLIMIT: rsize = U_SIZE_EMRSETMITERLIMIT; break; + case U_EMR_BEGINPATH: rsize = U_SIZE_EMRBEGINPATH; break; + case U_EMR_ENDPATH: rsize = U_SIZE_EMRENDPATH; break; + case U_EMR_CLOSEFIGURE: rsize = U_SIZE_EMRCLOSEFIGURE; break; + case U_EMR_FILLPATH: rsize = U_SIZE_EMRFILLPATH; break; + case U_EMR_STROKEANDFILLPATH: rsize = U_SIZE_EMRSTROKEANDFILLPATH; break; + case U_EMR_STROKEPATH: rsize = U_SIZE_EMRSTROKEPATH; break; + case U_EMR_FLATTENPATH: rsize = U_SIZE_EMRFLATTENPATH; break; + case U_EMR_WIDENPATH: rsize = U_SIZE_EMRWIDENPATH; break; + case U_EMR_SELECTCLIPPATH: rsize = U_SIZE_EMRSELECTCLIPPATH; break; + case U_EMR_ABORTPATH: rsize = U_SIZE_EMRABORTPATH; break; + case U_EMR_UNDEF69: rsize = U_SIZE_EMRUNDEFINED; break; + case U_EMR_COMMENT: rsize = U_SIZE_EMRCOMMENT; break; + case U_EMR_FILLRGN: rsize = U_SIZE_EMRFILLRGN; break; + case U_EMR_FRAMERGN: rsize = U_SIZE_EMRFRAMERGN; break; + case U_EMR_INVERTRGN: rsize = U_SIZE_EMRINVERTRGN; break; + case U_EMR_PAINTRGN: rsize = U_SIZE_EMRPAINTRGN; break; + case U_EMR_EXTSELECTCLIPRGN: rsize = U_SIZE_EMREXTSELECTCLIPRGN; break; + case U_EMR_BITBLT: rsize = U_SIZE_EMRBITBLT; break; + case U_EMR_STRETCHBLT: rsize = U_SIZE_EMRSTRETCHBLT; break; + case U_EMR_MASKBLT: rsize = U_SIZE_EMRMASKBLT; break; + case U_EMR_PLGBLT: rsize = U_SIZE_EMRPLGBLT; break; + case U_EMR_SETDIBITSTODEVICE: rsize = U_SIZE_EMRSETDIBITSTODEVICE; break; + case U_EMR_STRETCHDIBITS: rsize = U_SIZE_EMRSTRETCHDIBITS; break; + case U_EMR_EXTCREATEFONTINDIRECTW: rsize = U_SIZE_EMREXTCREATEFONTINDIRECTW_LOGFONT; break; + case U_EMR_EXTTEXTOUTA: rsize = U_SIZE_EMREXTTEXTOUTA; break; + case U_EMR_EXTTEXTOUTW: rsize = U_SIZE_EMREXTTEXTOUTW; break; + case U_EMR_POLYBEZIER16: rsize = U_SIZE_EMRPOLYBEZIER16; break; + case U_EMR_POLYGON16: rsize = U_SIZE_EMRPOLYGON16; break; + case U_EMR_POLYLINE16: rsize = U_SIZE_EMRPOLYLINE16; break; + case U_EMR_POLYBEZIERTO16: rsize = U_SIZE_EMRPOLYBEZIERTO16; break; + case U_EMR_POLYLINETO16: rsize = U_SIZE_EMRPOLYLINETO16; break; + case U_EMR_POLYPOLYLINE16: rsize = U_SIZE_EMRPOLYPOLYLINE16; break; + case U_EMR_POLYPOLYGON16: rsize = U_SIZE_EMRPOLYPOLYGON16; break; + case U_EMR_POLYDRAW16: rsize = U_SIZE_EMRPOLYDRAW16; break; + case U_EMR_CREATEMONOBRUSH: rsize = U_SIZE_EMRCREATEMONOBRUSH; break; + case U_EMR_CREATEDIBPATTERNBRUSHPT: rsize = U_SIZE_EMRCREATEDIBPATTERNBRUSHPT; break; + case U_EMR_EXTCREATEPEN: rsize = U_SIZE_EMREXTCREATEPEN; break; + case U_EMR_POLYTEXTOUTA: rsize = U_SIZE_EMRPOLYTEXTOUTA; break; + case U_EMR_POLYTEXTOUTW: rsize = U_SIZE_EMRPOLYTEXTOUTW; break; + case U_EMR_SETICMMODE: rsize = U_SIZE_EMRSETICMMODE; break; + case U_EMR_CREATECOLORSPACE: rsize = U_SIZE_EMRCREATECOLORSPACE; break; + case U_EMR_SETCOLORSPACE: rsize = U_SIZE_EMRSETCOLORSPACE; break; + case U_EMR_DELETECOLORSPACE: rsize = U_SIZE_EMRDELETECOLORSPACE; break; + case U_EMR_GLSRECORD: rsize = U_SIZE_EMRGLSRECORD; break; + case U_EMR_GLSBOUNDEDRECORD: rsize = U_SIZE_EMRGLSBOUNDEDRECORD; break; + case U_EMR_PIXELFORMAT: rsize = U_SIZE_EMRPIXELFORMAT; break; + case U_EMR_DRAWESCAPE: rsize = U_SIZE_EMRDRAWESCAPE; break; + case U_EMR_EXTESCAPE: rsize = U_SIZE_EMREXTESCAPE; break; + case U_EMR_UNDEF107: rsize = U_SIZE_EMRUNDEFINED; break; + case U_EMR_SMALLTEXTOUT: rsize = U_SIZE_EMRSMALLTEXTOUT; break; +// case U_EMR_FORCEUFIMAPPING: rsize = U_SIZE_EMRFORCEUFIMAPPING; break; + case U_EMR_NAMEDESCAPE: rsize = U_SIZE_EMRNAMEDESCAPE; break; +// case U_EMR_COLORCORRECTPALETTE: rsize = U_SIZE_EMRCOLORCORRECTPALETTE; break; +// case U_EMR_SETICMPROFILEA: rsize = U_SIZE_EMRSETICMPROFILEA; break; +// case U_EMR_SETICMPROFILEW: rsize = U_SIZE_EMRSETICMPROFILEW; break; + case U_EMR_ALPHABLEND: rsize = U_SIZE_EMRALPHABLEND; break; + case U_EMR_SETLAYOUT: rsize = U_SIZE_EMRSETLAYOUT; break; + case U_EMR_TRANSPARENTBLT: rsize = U_SIZE_EMRTRANSPARENTBLT; break; + case U_EMR_UNDEF117: rsize = U_SIZE_EMRUNDEFINED; break; + case U_EMR_GRADIENTFILL: rsize = U_SIZE_EMRGRADIENTFILL; break; +// case U_EMR_SETLINKEDUFIS: rsize = U_SIZE_EMRSETLINKEDUFIS; break; +// case U_EMR_SETTEXTJUSTIFICATION: rsize = U_SIZE_EMRSETTEXTJUSTIFICATION; break; + case U_EMR_COLORMATCHTOTARGETW: rsize = U_SIZE_EMRCOLORMATCHTOTARGETW; break; + case U_EMR_CREATECOLORSPACEW: rsize = U_SIZE_EMRCREATECOLORSPACEW; break; + default: rsize = U_SIZE_EMRNOTIMPLEMENTED; break; + } //end of switch + // record's declared size must be more than the minimum size for this type of record + if(*nSize < rsize){ + return(0); + } + return(1); +} + /** \brief Convert an entire EMF in memory from Big Endian to Little Endian. @@ -1622,155 +2113,151 @@ int U_emf_endian(char *contents, size_t length, int torev){ uint32_t off; uint32_t OK, recnum, iType; char *record; - PU_ENHMETARECORD pEmr; + const char *blimit = contents + length; /* should never wrap since it describes a structure in memory */ + int rstatus=1; record = contents; OK = 1; off = 0; recnum = 0; while(OK){ - if(record > contents + length){ // this is most likely a corrupt EMF - return(0); - } - pEmr = (PU_ENHMETARECORD)(record); - - iType = pEmr->iType; - off = pEmr->nSize; - if(!torev){ - U_swap4(&iType,1); - U_swap4(&off,1); + if(!U_emf_record_sizeok(record, blimit, &off, &iType, torev)){ + return(0); } switch (iType) { - case U_EMR_HEADER: U_EMRHEADER_swap(record, torev); break; - case U_EMR_POLYBEZIER: U_EMRPOLYBEZIER_swap(record, torev); break; - case U_EMR_POLYGON: U_EMRPOLYGON_swap(record, torev); break; - case U_EMR_POLYLINE: U_EMRPOLYLINE_swap(record, torev); break; - case U_EMR_POLYBEZIERTO: U_EMRPOLYBEZIERTO_swap(record, torev); break; - case U_EMR_POLYLINETO: U_EMRPOLYLINETO_swap(record, torev); break; - case U_EMR_POLYPOLYLINE: U_EMRPOLYPOLYLINE_swap(record, torev); break; - case U_EMR_POLYPOLYGON: U_EMRPOLYPOLYGON_swap(record, torev); break; - case U_EMR_SETWINDOWEXTEX: U_EMRSETWINDOWEXTEX_swap(record, torev); break; - case U_EMR_SETWINDOWORGEX: U_EMRSETWINDOWORGEX_swap(record, torev); break; - case U_EMR_SETVIEWPORTEXTEX: U_EMRSETVIEWPORTEXTEX_swap(record, torev); break; - case U_EMR_SETVIEWPORTORGEX: U_EMRSETVIEWPORTORGEX_swap(record, torev); break; - case U_EMR_SETBRUSHORGEX: U_EMRSETBRUSHORGEX_swap(record, torev); break; + case U_EMR_HEADER: rstatus=U_EMRHEADER_swap(record, torev); break; + case U_EMR_POLYBEZIER: rstatus=U_EMRPOLYBEZIER_swap(record, torev); break; + case U_EMR_POLYGON: rstatus=U_EMRPOLYGON_swap(record, torev); break; + case U_EMR_POLYLINE: rstatus=U_EMRPOLYLINE_swap(record, torev); break; + case U_EMR_POLYBEZIERTO: rstatus=U_EMRPOLYBEZIERTO_swap(record, torev); break; + case U_EMR_POLYLINETO: rstatus=U_EMRPOLYLINETO_swap(record, torev); break; + case U_EMR_POLYPOLYLINE: rstatus=U_EMRPOLYPOLYLINE_swap(record, torev); break; + case U_EMR_POLYPOLYGON: rstatus=U_EMRPOLYPOLYGON_swap(record, torev); break; + case U_EMR_SETWINDOWEXTEX: rstatus=U_EMRSETWINDOWEXTEX_swap(record, torev); break; + case U_EMR_SETWINDOWORGEX: rstatus=U_EMRSETWINDOWORGEX_swap(record, torev); break; + case U_EMR_SETVIEWPORTEXTEX: rstatus=U_EMRSETVIEWPORTEXTEX_swap(record, torev); break; + case U_EMR_SETVIEWPORTORGEX: rstatus=U_EMRSETVIEWPORTORGEX_swap(record, torev); break; + case U_EMR_SETBRUSHORGEX: rstatus=U_EMRSETBRUSHORGEX_swap(record, torev); break; case U_EMR_EOF: - U_EMREOF_swap(record, torev); + rstatus=U_EMREOF_swap(record, torev); OK = 0; /* Exit triggered here */ break; - case U_EMR_SETPIXELV: U_EMRSETPIXELV_swap(record, torev); break; - case U_EMR_SETMAPPERFLAGS: U_EMRSETMAPPERFLAGS_swap(record, torev); break; - case U_EMR_SETMAPMODE: U_EMRSETMAPMODE_swap(record, torev); break; - case U_EMR_SETBKMODE: U_EMRSETBKMODE_swap(record, torev); break; - case U_EMR_SETPOLYFILLMODE: U_EMRSETPOLYFILLMODE_swap(record, torev); break; - case U_EMR_SETROP2: U_EMRSETROP2_swap(record, torev); break; - case U_EMR_SETSTRETCHBLTMODE: U_EMRSETSTRETCHBLTMODE_swap(record, torev); break; - case U_EMR_SETTEXTALIGN: U_EMRSETTEXTALIGN_swap(record, torev); break; - case U_EMR_SETCOLORADJUSTMENT: U_EMRSETCOLORADJUSTMENT_swap(record, torev); break; - case U_EMR_SETTEXTCOLOR: U_EMRSETTEXTCOLOR_swap(record, torev); break; - case U_EMR_SETBKCOLOR: U_EMRSETBKCOLOR_swap(record, torev); break; - case U_EMR_OFFSETCLIPRGN: U_EMROFFSETCLIPRGN_swap(record, torev); break; - case U_EMR_MOVETOEX: U_EMRMOVETOEX_swap(record, torev); break; - case U_EMR_SETMETARGN: U_EMRSETMETARGN_swap(record, torev); break; - case U_EMR_EXCLUDECLIPRECT: U_EMREXCLUDECLIPRECT_swap(record, torev); break; - case U_EMR_INTERSECTCLIPRECT: U_EMRINTERSECTCLIPRECT_swap(record, torev); break; - case U_EMR_SCALEVIEWPORTEXTEX: U_EMRSCALEVIEWPORTEXTEX_swap(record, torev); break; - case U_EMR_SCALEWINDOWEXTEX: U_EMRSCALEWINDOWEXTEX_swap(record, torev); break; - case U_EMR_SAVEDC: U_EMRSAVEDC_swap(record, torev); break; - case U_EMR_RESTOREDC: U_EMRRESTOREDC_swap(record, torev); break; - case U_EMR_SETWORLDTRANSFORM: U_EMRSETWORLDTRANSFORM_swap(record, torev); break; - case U_EMR_MODIFYWORLDTRANSFORM: U_EMRMODIFYWORLDTRANSFORM_swap(record, torev); break; - case U_EMR_SELECTOBJECT: U_EMRSELECTOBJECT_swap(record, torev); break; - case U_EMR_CREATEPEN: U_EMRCREATEPEN_swap(record, torev); break; - case U_EMR_CREATEBRUSHINDIRECT: U_EMRCREATEBRUSHINDIRECT_swap(record, torev); break; - case U_EMR_DELETEOBJECT: U_EMRDELETEOBJECT_swap(record, torev); break; - case U_EMR_ANGLEARC: U_EMRANGLEARC_swap(record, torev); break; - case U_EMR_ELLIPSE: U_EMRELLIPSE_swap(record, torev); break; - case U_EMR_RECTANGLE: U_EMRRECTANGLE_swap(record, torev); break; - case U_EMR_ROUNDRECT: U_EMRROUNDRECT_swap(record, torev); break; - case U_EMR_ARC: U_EMRARC_swap(record, torev); break; - case U_EMR_CHORD: U_EMRCHORD_swap(record, torev); break; - case U_EMR_PIE: U_EMRPIE_swap(record, torev); break; - case U_EMR_SELECTPALETTE: U_EMRSELECTPALETTE_swap(record, torev); break; - case U_EMR_CREATEPALETTE: U_EMRCREATEPALETTE_swap(record, torev); break; - case U_EMR_SETPALETTEENTRIES: U_EMRSETPALETTEENTRIES_swap(record, torev); break; - case U_EMR_RESIZEPALETTE: U_EMRRESIZEPALETTE_swap(record, torev); break; - case U_EMR_REALIZEPALETTE: U_EMRREALIZEPALETTE_swap(record, torev); break; - case U_EMR_EXTFLOODFILL: U_EMREXTFLOODFILL_swap(record, torev); break; - case U_EMR_LINETO: U_EMRLINETO_swap(record, torev); break; - case U_EMR_ARCTO: U_EMRARCTO_swap(record, torev); break; - case U_EMR_POLYDRAW: U_EMRPOLYDRAW_swap(record, torev); break; - case U_EMR_SETARCDIRECTION: U_EMRSETARCDIRECTION_swap(record, torev); break; - case U_EMR_SETMITERLIMIT: U_EMRSETMITERLIMIT_swap(record, torev); break; - case U_EMR_BEGINPATH: U_EMRBEGINPATH_swap(record, torev); break; - case U_EMR_ENDPATH: U_EMRENDPATH_swap(record, torev); break; - case U_EMR_CLOSEFIGURE: U_EMRCLOSEFIGURE_swap(record, torev); break; - case U_EMR_FILLPATH: U_EMRFILLPATH_swap(record, torev); break; - case U_EMR_STROKEANDFILLPATH: U_EMRSTROKEANDFILLPATH_swap(record, torev); break; - case U_EMR_STROKEPATH: U_EMRSTROKEPATH_swap(record, torev); break; - case U_EMR_FLATTENPATH: U_EMRFLATTENPATH_swap(record, torev); break; - case U_EMR_WIDENPATH: U_EMRWIDENPATH_swap(record, torev); break; - case U_EMR_SELECTCLIPPATH: U_EMRSELECTCLIPPATH_swap(record, torev); break; - case U_EMR_ABORTPATH: U_EMRABORTPATH_swap(record, torev); break; - case U_EMR_UNDEF69: U_EMRUNDEF69_swap(record, torev); break; - case U_EMR_COMMENT: U_EMRCOMMENT_swap(record, torev); break; - case U_EMR_FILLRGN: U_EMRFILLRGN_swap(record, torev); break; - case U_EMR_FRAMERGN: U_EMRFRAMERGN_swap(record, torev); break; - case U_EMR_INVERTRGN: U_EMRINVERTRGN_swap(record, torev); break; - case U_EMR_PAINTRGN: U_EMRPAINTRGN_swap(record, torev); break; - case U_EMR_EXTSELECTCLIPRGN: U_EMREXTSELECTCLIPRGN_swap(record, torev); break; - case U_EMR_BITBLT: U_EMRBITBLT_swap(record, torev); break; - case U_EMR_STRETCHBLT: U_EMRSTRETCHBLT_swap(record, torev); break; - case U_EMR_MASKBLT: U_EMRMASKBLT_swap(record, torev); break; - case U_EMR_PLGBLT: U_EMRPLGBLT_swap(record, torev); break; - case U_EMR_SETDIBITSTODEVICE: U_EMRSETDIBITSTODEVICE_swap(record, torev); break; - case U_EMR_STRETCHDIBITS: U_EMRSTRETCHDIBITS_swap(record, torev); break; - case U_EMR_EXTCREATEFONTINDIRECTW: U_EMREXTCREATEFONTINDIRECTW_swap(record, torev); break; - case U_EMR_EXTTEXTOUTA: U_EMREXTTEXTOUTA_swap(record, torev); break; - case U_EMR_EXTTEXTOUTW: U_EMREXTTEXTOUTW_swap(record, torev); break; - case U_EMR_POLYBEZIER16: U_EMRPOLYBEZIER16_swap(record, torev); break; - case U_EMR_POLYGON16: U_EMRPOLYGON16_swap(record, torev); break; - case U_EMR_POLYLINE16: U_EMRPOLYLINE16_swap(record, torev); break; - case U_EMR_POLYBEZIERTO16: U_EMRPOLYBEZIERTO16_swap(record, torev); break; - case U_EMR_POLYLINETO16: U_EMRPOLYLINETO16_swap(record, torev); break; - case U_EMR_POLYPOLYLINE16: U_EMRPOLYPOLYLINE16_swap(record, torev); break; - case U_EMR_POLYPOLYGON16: U_EMRPOLYPOLYGON16_swap(record, torev); break; - case U_EMR_POLYDRAW16: U_EMRPOLYDRAW16_swap(record, torev); break; - case U_EMR_CREATEMONOBRUSH: U_EMRCREATEMONOBRUSH_swap(record, torev); break; - case U_EMR_CREATEDIBPATTERNBRUSHPT: U_EMRCREATEDIBPATTERNBRUSHPT_swap(record, torev); break; - case U_EMR_EXTCREATEPEN: U_EMREXTCREATEPEN_swap(record, torev); break; - case U_EMR_POLYTEXTOUTA: U_EMRPOLYTEXTOUTA_swap(record, torev); break; - case U_EMR_POLYTEXTOUTW: U_EMRPOLYTEXTOUTW_swap(record, torev); break; - case U_EMR_SETICMMODE: U_EMRSETICMMODE_swap(record, torev); break; - case U_EMR_CREATECOLORSPACE: U_EMRCREATECOLORSPACE_swap(record, torev); break; - case U_EMR_SETCOLORSPACE: U_EMRSETCOLORSPACE_swap(record, torev); break; - case U_EMR_DELETECOLORSPACE: U_EMRDELETECOLORSPACE_swap(record, torev); break; - case U_EMR_GLSRECORD: U_EMRGLSRECORD_swap(record, torev); break; - case U_EMR_GLSBOUNDEDRECORD: U_EMRGLSBOUNDEDRECORD_swap(record, torev); break; - case U_EMR_PIXELFORMAT: U_EMRPIXELFORMAT_swap(record, torev); break; - case U_EMR_DRAWESCAPE: U_EMRDRAWESCAPE_swap(record, torev); break; - case U_EMR_EXTESCAPE: U_EMREXTESCAPE_swap(record, torev); break; - case U_EMR_UNDEF107: U_EMRUNDEF107_swap(record, torev); break; - case U_EMR_SMALLTEXTOUT: U_EMRSMALLTEXTOUT_swap(record, torev); break; - case U_EMR_FORCEUFIMAPPING: U_EMRFORCEUFIMAPPING_swap(record, torev); break; - case U_EMR_NAMEDESCAPE: U_EMRNAMEDESCAPE_swap(record, torev); break; - case U_EMR_COLORCORRECTPALETTE: U_EMRCOLORCORRECTPALETTE_swap(record, torev); break; - case U_EMR_SETICMPROFILEA: U_EMRSETICMPROFILEA_swap(record, torev); break; - case U_EMR_SETICMPROFILEW: U_EMRSETICMPROFILEW_swap(record, torev); break; - case U_EMR_ALPHABLEND: U_EMRALPHABLEND_swap(record, torev); break; - case U_EMR_SETLAYOUT: U_EMRSETLAYOUT_swap(record, torev); break; - case U_EMR_TRANSPARENTBLT: U_EMRTRANSPARENTBLT_swap(record, torev); break; - case U_EMR_UNDEF117: U_EMRUNDEF117_swap(record, torev); break; - case U_EMR_GRADIENTFILL: U_EMRGRADIENTFILL_swap(record, torev); break; - case U_EMR_SETLINKEDUFIS: U_EMRSETLINKEDUFIS_swap(record, torev); break; - case U_EMR_SETTEXTJUSTIFICATION: U_EMRSETTEXTJUSTIFICATION_swap(record, torev); break; - case U_EMR_COLORMATCHTOTARGETW: U_EMRCOLORMATCHTOTARGETW_swap(record, torev); break; - case U_EMR_CREATECOLORSPACEW: U_EMRCREATECOLORSPACEW_swap(record, torev); break; - default: U_EMRNOTIMPLEMENTED_swap(record, torev); break; + case U_EMR_SETPIXELV: rstatus=U_EMRSETPIXELV_swap(record, torev); break; + case U_EMR_SETMAPPERFLAGS: rstatus=U_EMRSETMAPPERFLAGS_swap(record, torev); break; + case U_EMR_SETMAPMODE: rstatus=U_EMRSETMAPMODE_swap(record, torev); break; + case U_EMR_SETBKMODE: rstatus=U_EMRSETBKMODE_swap(record, torev); break; + case U_EMR_SETPOLYFILLMODE: rstatus=U_EMRSETPOLYFILLMODE_swap(record, torev); break; + case U_EMR_SETROP2: rstatus=U_EMRSETROP2_swap(record, torev); break; + case U_EMR_SETSTRETCHBLTMODE: rstatus=U_EMRSETSTRETCHBLTMODE_swap(record, torev); break; + case U_EMR_SETTEXTALIGN: rstatus=U_EMRSETTEXTALIGN_swap(record, torev); break; + case U_EMR_SETCOLORADJUSTMENT: rstatus=U_EMRSETCOLORADJUSTMENT_swap(record, torev); break; + case U_EMR_SETTEXTCOLOR: rstatus=U_EMRSETTEXTCOLOR_swap(record, torev); break; + case U_EMR_SETBKCOLOR: rstatus=U_EMRSETBKCOLOR_swap(record, torev); break; + case U_EMR_OFFSETCLIPRGN: rstatus=U_EMROFFSETCLIPRGN_swap(record, torev); break; + case U_EMR_MOVETOEX: rstatus=U_EMRMOVETOEX_swap(record, torev); break; + case U_EMR_SETMETARGN: rstatus=U_EMRSETMETARGN_swap(record, torev); break; + case U_EMR_EXCLUDECLIPRECT: rstatus=U_EMREXCLUDECLIPRECT_swap(record, torev); break; + case U_EMR_INTERSECTCLIPRECT: rstatus=U_EMRINTERSECTCLIPRECT_swap(record, torev); break; + case U_EMR_SCALEVIEWPORTEXTEX: rstatus=U_EMRSCALEVIEWPORTEXTEX_swap(record, torev); break; + case U_EMR_SCALEWINDOWEXTEX: rstatus=U_EMRSCALEWINDOWEXTEX_swap(record, torev); break; + case U_EMR_SAVEDC: rstatus=U_EMRSAVEDC_swap(record, torev); break; + case U_EMR_RESTOREDC: rstatus=U_EMRRESTOREDC_swap(record, torev); break; + case U_EMR_SETWORLDTRANSFORM: rstatus=U_EMRSETWORLDTRANSFORM_swap(record, torev); break; + case U_EMR_MODIFYWORLDTRANSFORM: rstatus=U_EMRMODIFYWORLDTRANSFORM_swap(record, torev); break; + case U_EMR_SELECTOBJECT: rstatus=U_EMRSELECTOBJECT_swap(record, torev); break; + case U_EMR_CREATEPEN: rstatus=U_EMRCREATEPEN_swap(record, torev); break; + case U_EMR_CREATEBRUSHINDIRECT: rstatus=U_EMRCREATEBRUSHINDIRECT_swap(record, torev); break; + case U_EMR_DELETEOBJECT: rstatus=U_EMRDELETEOBJECT_swap(record, torev); break; + case U_EMR_ANGLEARC: rstatus=U_EMRANGLEARC_swap(record, torev); break; + case U_EMR_ELLIPSE: rstatus=U_EMRELLIPSE_swap(record, torev); break; + case U_EMR_RECTANGLE: rstatus=U_EMRRECTANGLE_swap(record, torev); break; + case U_EMR_ROUNDRECT: rstatus=U_EMRROUNDRECT_swap(record, torev); break; + case U_EMR_ARC: rstatus=U_EMRARC_swap(record, torev); break; + case U_EMR_CHORD: rstatus=U_EMRCHORD_swap(record, torev); break; + case U_EMR_PIE: rstatus=U_EMRPIE_swap(record, torev); break; + case U_EMR_SELECTPALETTE: rstatus=U_EMRSELECTPALETTE_swap(record, torev); break; + case U_EMR_CREATEPALETTE: rstatus=U_EMRCREATEPALETTE_swap(record, torev); break; + case U_EMR_SETPALETTEENTRIES: rstatus=U_EMRSETPALETTEENTRIES_swap(record, torev); break; + case U_EMR_RESIZEPALETTE: rstatus=U_EMRRESIZEPALETTE_swap(record, torev); break; + case U_EMR_REALIZEPALETTE: rstatus=U_EMRREALIZEPALETTE_swap(record, torev); break; + case U_EMR_EXTFLOODFILL: rstatus=U_EMREXTFLOODFILL_swap(record, torev); break; + case U_EMR_LINETO: rstatus=U_EMRLINETO_swap(record, torev); break; + case U_EMR_ARCTO: rstatus=U_EMRARCTO_swap(record, torev); break; + case U_EMR_POLYDRAW: rstatus=U_EMRPOLYDRAW_swap(record, torev); break; + case U_EMR_SETARCDIRECTION: rstatus=U_EMRSETARCDIRECTION_swap(record, torev); break; + case U_EMR_SETMITERLIMIT: rstatus=U_EMRSETMITERLIMIT_swap(record, torev); break; + case U_EMR_BEGINPATH: rstatus=U_EMRBEGINPATH_swap(record, torev); break; + case U_EMR_ENDPATH: rstatus=U_EMRENDPATH_swap(record, torev); break; + case U_EMR_CLOSEFIGURE: rstatus=U_EMRCLOSEFIGURE_swap(record, torev); break; + case U_EMR_FILLPATH: rstatus=U_EMRFILLPATH_swap(record, torev); break; + case U_EMR_STROKEANDFILLPATH: rstatus=U_EMRSTROKEANDFILLPATH_swap(record, torev); break; + case U_EMR_STROKEPATH: rstatus=U_EMRSTROKEPATH_swap(record, torev); break; + case U_EMR_FLATTENPATH: rstatus=U_EMRFLATTENPATH_swap(record, torev); break; + case U_EMR_WIDENPATH: rstatus=U_EMRWIDENPATH_swap(record, torev); break; + case U_EMR_SELECTCLIPPATH: rstatus=U_EMRSELECTCLIPPATH_swap(record, torev); break; + case U_EMR_ABORTPATH: rstatus=U_EMRABORTPATH_swap(record, torev); break; + case U_EMR_UNDEF69: rstatus=U_EMRUNDEF69_swap(record, torev); break; + case U_EMR_COMMENT: rstatus=U_EMRCOMMENT_swap(record, torev); break; + case U_EMR_FILLRGN: rstatus=U_EMRFILLRGN_swap(record, torev); break; + case U_EMR_FRAMERGN: rstatus=U_EMRFRAMERGN_swap(record, torev); break; + case U_EMR_INVERTRGN: rstatus=U_EMRINVERTRGN_swap(record, torev); break; + case U_EMR_PAINTRGN: rstatus=U_EMRPAINTRGN_swap(record, torev); break; + case U_EMR_EXTSELECTCLIPRGN: rstatus=U_EMREXTSELECTCLIPRGN_swap(record, torev);break; + case U_EMR_BITBLT: rstatus=U_EMRBITBLT_swap(record, torev); break; + case U_EMR_STRETCHBLT: rstatus=U_EMRSTRETCHBLT_swap(record, torev); break; + case U_EMR_MASKBLT: rstatus=U_EMRMASKBLT_swap(record, torev); break; + case U_EMR_PLGBLT: rstatus=U_EMRPLGBLT_swap(record, torev); break; + case U_EMR_SETDIBITSTODEVICE: rstatus=U_EMRSETDIBITSTODEVICE_swap(record, torev); break; + case U_EMR_STRETCHDIBITS: rstatus=U_EMRSTRETCHDIBITS_swap(record, torev); break; + case U_EMR_EXTCREATEFONTINDIRECTW: rstatus=U_EMREXTCREATEFONTINDIRECTW_swap(record, torev); break; + case U_EMR_EXTTEXTOUTA: rstatus=U_EMREXTTEXTOUTA_swap(record, torev); break; + case U_EMR_EXTTEXTOUTW: rstatus=U_EMREXTTEXTOUTW_swap(record, torev); break; + case U_EMR_POLYBEZIER16: rstatus=U_EMRPOLYBEZIER16_swap(record, torev); break; + case U_EMR_POLYGON16: rstatus=U_EMRPOLYGON16_swap(record, torev); break; + case U_EMR_POLYLINE16: rstatus=U_EMRPOLYLINE16_swap(record, torev); break; + case U_EMR_POLYBEZIERTO16: rstatus=U_EMRPOLYBEZIERTO16_swap(record, torev); break; + case U_EMR_POLYLINETO16: rstatus=U_EMRPOLYLINETO16_swap(record, torev); break; + case U_EMR_POLYPOLYLINE16: rstatus=U_EMRPOLYPOLYLINE16_swap(record, torev); break; + case U_EMR_POLYPOLYGON16: rstatus=U_EMRPOLYPOLYGON16_swap(record, torev); break; + case U_EMR_POLYDRAW16: rstatus=U_EMRPOLYDRAW16_swap(record, torev); break; + case U_EMR_CREATEMONOBRUSH: rstatus=U_EMRCREATEMONOBRUSH_swap(record, torev); break; + case U_EMR_CREATEDIBPATTERNBRUSHPT: rstatus=U_EMRCREATEDIBPATTERNBRUSHPT_swap(record, torev); break; + case U_EMR_EXTCREATEPEN: rstatus=U_EMREXTCREATEPEN_swap(record, torev); break; + case U_EMR_POLYTEXTOUTA: rstatus=U_EMRPOLYTEXTOUTA_swap(record, torev); break; + case U_EMR_POLYTEXTOUTW: rstatus=U_EMRPOLYTEXTOUTW_swap(record, torev); break; + case U_EMR_SETICMMODE: rstatus=U_EMRSETICMMODE_swap(record, torev); break; + case U_EMR_CREATECOLORSPACE: rstatus=U_EMRCREATECOLORSPACE_swap(record, torev); break; + case U_EMR_SETCOLORSPACE: rstatus=U_EMRSETCOLORSPACE_swap(record, torev); break; + case U_EMR_DELETECOLORSPACE: rstatus=U_EMRDELETECOLORSPACE_swap(record, torev); break; + case U_EMR_GLSRECORD: rstatus=U_EMRGLSRECORD_swap(record, torev); break; + case U_EMR_GLSBOUNDEDRECORD: rstatus=U_EMRGLSBOUNDEDRECORD_swap(record, torev); break; + case U_EMR_PIXELFORMAT: rstatus=U_EMRPIXELFORMAT_swap(record, torev); break; + case U_EMR_DRAWESCAPE: rstatus=U_EMRDRAWESCAPE_swap(record, torev); break; + case U_EMR_EXTESCAPE: rstatus=U_EMREXTESCAPE_swap(record, torev); break; + case U_EMR_UNDEF107: rstatus=U_EMRUNDEF107_swap(record, torev); break; + case U_EMR_SMALLTEXTOUT: rstatus=U_EMRSMALLTEXTOUT_swap(record, torev); break; + case U_EMR_FORCEUFIMAPPING: rstatus=U_EMRFORCEUFIMAPPING_swap(record, torev); break; + case U_EMR_NAMEDESCAPE: rstatus=U_EMRNAMEDESCAPE_swap(record, torev); break; + case U_EMR_COLORCORRECTPALETTE: rstatus=U_EMRCOLORCORRECTPALETTE_swap(record, torev); break; + case U_EMR_SETICMPROFILEA: rstatus=U_EMRSETICMPROFILEA_swap(record, torev); break; + case U_EMR_SETICMPROFILEW: rstatus=U_EMRSETICMPROFILEW_swap(record, torev); break; + case U_EMR_ALPHABLEND: rstatus=U_EMRALPHABLEND_swap(record, torev); break; + case U_EMR_SETLAYOUT: rstatus=U_EMRSETLAYOUT_swap(record, torev); break; + case U_EMR_TRANSPARENTBLT: rstatus=U_EMRTRANSPARENTBLT_swap(record, torev); break; + case U_EMR_UNDEF117: rstatus=U_EMRUNDEF117_swap(record, torev); break; + case U_EMR_GRADIENTFILL: rstatus=U_EMRGRADIENTFILL_swap(record, torev); break; + case U_EMR_SETLINKEDUFIS: rstatus=U_EMRSETLINKEDUFIS_swap(record, torev); break; + case U_EMR_SETTEXTJUSTIFICATION: rstatus=U_EMRSETTEXTJUSTIFICATION_swap(record, torev); break; + case U_EMR_COLORMATCHTOTARGETW: rstatus=U_EMRCOLORMATCHTOTARGETW_swap(record, torev); break; + case U_EMR_CREATECOLORSPACEW: rstatus=U_EMRCREATECOLORSPACEW_swap(record, torev); break; + default: rstatus=U_EMRNOTIMPLEMENTED_swap(record, torev); break; } //end of switch + if(!rstatus){ + return(rstatus); + } record += off; recnum++; } //end of while diff --git a/src/libuemf/uemf_endian.h b/src/libuemf/uemf_endian.h index 317968c34..9dbcec455 100644 --- a/src/libuemf/uemf_endian.h +++ b/src/libuemf/uemf_endian.h @@ -6,11 +6,11 @@ /* File: uemf_endian.h -Version: 0.0.3 -Date: 24-JUL-2012 +Version: 0.0.4 +Date: 24-MAR-2015 Author: David Mathog, Biology Division, Caltech email: mathog@caltech.edu -Copyright: 2012 David Mathog and California Institute of Technology (Caltech) +Copyright: 2015 David Mathog and California Institute of Technology (Caltech) */ #ifndef _UEMF_ENDIAN_ @@ -49,6 +49,7 @@ extern "C" { //! \cond // prototypes int U_emf_endian(char *contents, size_t length, int torev); +int U_emf_record_sizeok(const char *record, const char *blimit, uint32_t *nSize, uint32_t *iType, int torev); //! \endcond #ifdef __cplusplus diff --git a/src/libuemf/uemf_print.c b/src/libuemf/uemf_print.c index aa574bd73..4bc9f0206 100644 --- a/src/libuemf/uemf_print.c +++ b/src/libuemf/uemf_print.c @@ -6,11 +6,11 @@ /* File: uemf_print.c -Version: 0.0.15 -Date: 17-OCT-2013 +Version: 0.0.20 +Date: 21-MAY-2015 Author: David Mathog, Biology Division, Caltech email: mathog@caltech.edu -Copyright: 2013 David Mathog and California Institute of Technology (Caltech) +Copyright: 2015 David Mathog and California Institute of Technology (Caltech) */ #ifdef __cplusplus @@ -23,15 +23,43 @@ extern "C" { #include <string.h> #include "uemf.h" #include "upmf_print.h" +#include "uemf_safe.h" //! \cond #define UNUSED(x) (void)(x) +/* This bit of code is used all over the place, so reduce it to a DEFINITION */ +#define IF_MEM_UNSAFE_PRINT_AND_RETURN(A,B,C) if(IS_MEM_UNSAFE(A,B,C)){printf(" record corruption HERE\n"); return; } /* one needed prototype */ void U_swap4(void *ul, unsigned int count); //! \endcond /** + \brief calculate a CRC32 value for record + \returns CRC32 value calculated for record + \param record pointer to the first byte + \param Size number of bytes in the record + +Code based on example crc32b here: + http://www.hackersdelight.org/hdcodetxt/crc.c.txt +*/ +uint32_t lu_crc32(const char *record, uint32_t Size){ + const unsigned char *message = record; + uint32_t i, j; + uint32_t crc, mask; + + crc = 0xFFFFFFFF; + for(i=0;i<Size;i++){ // over all bytes + crc = crc ^ *message++; + for (j = 0; j < 8; j++) { // over all bits + mask = -(crc & 1); + crc = (crc >> 1) ^ (0xEDB88320 & mask); + } + } + return ~crc; +} + +/** \brief Print some number of hex bytes \param buf pointer to the first byte \param num number of bytes @@ -42,6 +70,7 @@ void hexbytes_print(uint8_t *buf,unsigned int num){ } } + /* ********************************************************************************************** These functions print standard objects used in the EMR records. The low level ones do not append EOL. @@ -364,24 +393,28 @@ int bitmapinfoheader_print( /** \brief Print a Pointer to a U_BITMAPINFO object. \param Bmi Pointer to a U_BITMAPINFO object + \param blimit Pointer to the first byte after after this record This may be called from WMF _print routines, where problems could occur if the data was passed as the struct or a pointer to the struct, as the struct may not be aligned in memory. */ void bitmapinfo_print( - const char *Bmi + const char *Bmi, + const char *blimit ){ int i,k; int ClrUsed; U_RGBQUAD BmiColor; printf("BmiHeader: "); + IF_MEM_UNSAFE_PRINT_AND_RETURN(Bmi, offsetof(U_BITMAPINFO,bmiHeader) + sizeof(U_BITMAPINFOHEADER), blimit); ClrUsed = bitmapinfoheader_print(Bmi + offsetof(U_BITMAPINFO,bmiHeader)); if(ClrUsed){ - k= offsetof(U_BITMAPINFO,bmiColors); - for(i=0; i<ClrUsed; i++, k+= sizeof(U_RGBQUAD)){ - memcpy(&BmiColor, Bmi+k, sizeof(U_RGBQUAD)); - printf("%d:",i); rgbquad_print(BmiColor); - } + k= offsetof(U_BITMAPINFO,bmiColors); + IF_MEM_UNSAFE_PRINT_AND_RETURN(Bmi, offsetof(U_BITMAPINFO,bmiColors) + ClrUsed*sizeof(U_RGBQUAD), blimit); + for(i=0; i<ClrUsed; i++, k+= sizeof(U_RGBQUAD)){ + memcpy(&BmiColor, Bmi+k, sizeof(U_RGBQUAD)); + printf("%d:",i); rgbquad_print(BmiColor); + } } } @@ -484,15 +517,19 @@ void rgndataheader_print( /** \brief Print a pointer to a U_RGNDATA object. \param rd pointer to a U_RGNDATA object. + \param limit pointer that sets upper limit for data examination */ void rgndata_print( - PU_RGNDATA rd + PU_RGNDATA rd, + const char *blimit ){ unsigned int i; PU_RECTL rects; - printf("rdh:"); rgndataheader_print(rd->rdh ); + IF_MEM_UNSAFE_PRINT_AND_RETURN(rd, sizeof(U_RGNDATAHEADER), blimit); + printf("rdh: "); rgndataheader_print(rd->rdh ); printf(" rects: "); if(rd->rdh.nCount){ rects = (PU_RECTL) &(rd->Buffer); + IF_MEM_UNSAFE_PRINT_AND_RETURN(rects, rd->rdh.nCount * sizeof(U_RECTL), blimit); for(i=0;i<rd->rdh.nCount;i++){ printf("%d:",i); rectl_print(rects[i]); } @@ -559,11 +596,13 @@ void pixelformatdescriptor_print( \brief Print a Pointer to a U_EMRTEXT record \param emt Pointer to a U_EMRTEXT record \param record Pointer to the start of the record which contains this U_ERMTEXT + \param blimit Pointer to the first byte after after this record \param type 0 for 8 bit character, anything else for 16 */ void emrtext_print( const char *emt, const char *record, + const char *blimit, int type ){ unsigned int i,off; @@ -575,9 +614,11 @@ void emrtext_print( printf("offString:%u " ,pemt->offString ); if(pemt->offString){ if(!type){ + IF_MEM_UNSAFE_PRINT_AND_RETURN(record, pemt->offString + pemt->nChars*sizeof(char), blimit); printf("string8:<%s> ",record + pemt->offString); } else { + IF_MEM_UNSAFE_PRINT_AND_RETURN(record, pemt->offString + pemt->nChars*2*sizeof(char), blimit); string = U_Utf16leToUtf8((uint16_t *)(record + pemt->offString), pemt->nChars, NULL); printf("string16:<%s> ",string); free(string); @@ -622,9 +663,15 @@ void core1_print(const char *name, const char *contents){ unsigned int i; UNUSED(name); PU_EMRPOLYLINETO pEmr = (PU_EMRPOLYLINETO) (contents); + if(pEmr->emr.nSize < sizeof(U_EMRPOLYBEZIER)){ + printf(" record corruption HERE\n"); + return; + } + const char *blimit = contents + pEmr->emr.nSize; printf(" rclBounds: "); rectl_print(pEmr->rclBounds); printf("\n"); printf(" cptl: %d\n",pEmr->cptl ); printf(" Points: "); + IF_MEM_UNSAFE_PRINT_AND_RETURN(pEmr->aptl, pEmr->cptl*sizeof(U_POINTL), blimit); for(i=0;i<pEmr->cptl; i++){ printf("[%d]:",i); pointl_print(pEmr->aptl[i]); } @@ -636,16 +683,23 @@ void core2_print(const char *name, const char *contents){ unsigned int i; UNUSED(name); PU_EMRPOLYPOLYGON pEmr = (PU_EMRPOLYPOLYGON) (contents); + if(pEmr->emr.nSize < sizeof(U_EMRPOLYPOLYGON)){ + printf(" record corruption HERE\n"); + return; + } + const char *blimit = contents + pEmr->emr.nSize; printf(" rclBounds: "); rectl_print(pEmr->rclBounds); printf("\n"); printf(" nPolys: %d\n",pEmr->nPolys ); printf(" cptl: %d\n",pEmr->cptl ); printf(" Counts: "); + IF_MEM_UNSAFE_PRINT_AND_RETURN(pEmr->aPolyCounts, pEmr->nPolys*sizeof(U_POLYCOUNTS), blimit); for(i=0;i<pEmr->nPolys; i++){ printf(" [%d]:%d ",i,pEmr->aPolyCounts[i] ); } printf("\n"); PU_POINTL paptl = (PU_POINTL)((char *)pEmr->aPolyCounts + sizeof(uint32_t)* pEmr->nPolys); printf(" Points: "); + IF_MEM_UNSAFE_PRINT_AND_RETURN(paptl, pEmr->cptl*sizeof(U_POINTL), blimit); for(i=0;i<pEmr->cptl; i++){ printf(" [%d]:",i); pointl_print(paptl[i]); } @@ -656,7 +710,12 @@ void core2_print(const char *name, const char *contents){ // Functions with the same form starting with U_EMRSETMAPMODE_print void core3_print(const char *name, const char *label, const char *contents){ UNUSED(name); + /* access violation is impossible for these because there are no counts or offsets */ PU_EMRSETMAPMODE pEmr = (PU_EMRSETMAPMODE)(contents); + if(pEmr->emr.nSize < sizeof(U_EMRSETMAPMODE)){ + printf(" record corruption HERE\n"); + return; + } if(!strcmp(label,"crColor:")){ printf(" %-15s ",label); colorref_print(*(U_COLORREF *)&(pEmr->iMode)); printf("\n"); } @@ -672,6 +731,10 @@ void core3_print(const char *name, const char *label, const char *contents){ void core4_print(const char *name, const char *contents){ UNUSED(name); PU_EMRELLIPSE pEmr = (PU_EMRELLIPSE)( contents); + if(pEmr->emr.nSize < sizeof(U_EMRELLIPSE)){ + printf(" record corruption HERE\n"); + return; + } printf(" rclBox: "); rectl_print(pEmr->rclBox); printf("\n"); } @@ -680,10 +743,16 @@ void core6_print(const char *name, const char *contents){ UNUSED(name); unsigned int i; PU_EMRPOLYBEZIER16 pEmr = (PU_EMRPOLYBEZIER16) (contents); + if(pEmr->emr.nSize < sizeof(U_EMRPOLYBEZIER16)){ + printf(" record corruption HERE\n"); + return; + } + const char *blimit = contents + pEmr->emr.nSize; printf(" rclBounds: "); rectl_print(pEmr->rclBounds); printf("\n"); printf(" cpts: %d\n",pEmr->cpts ); printf(" Points: "); PU_POINT16 papts = (PU_POINT16)(&(pEmr->apts)); + IF_MEM_UNSAFE_PRINT_AND_RETURN(papts, pEmr->cpts*sizeof(U_POINT16), blimit); for(i=0; i<pEmr->cpts; i++){ printf(" [%d]:",i); point16_print(papts[i]); } @@ -698,6 +767,10 @@ void core6_print(const char *name, const char *contents){ void core7_print(const char *name, const char *field1, const char *field2, const char *contents){ UNUSED(name); PU_EMRGENERICPAIR pEmr = (PU_EMRGENERICPAIR) (contents); + if(pEmr->emr.nSize < sizeof(U_EMRGENERICPAIR)){ + printf(" record corruption HERE\n"); + return; + } if(*field2){ printf(" %-15s %d\n",field1,pEmr->pair.x); printf(" %-15s %d\n",field2,pEmr->pair.y); @@ -711,12 +784,13 @@ void core7_print(const char *name, const char *field1, const char *field2, const void core8_print(const char *name, const char *contents, int type){ UNUSED(name); PU_EMREXTTEXTOUTA pEmr = (PU_EMREXTTEXTOUTA) (contents); + const char *blimit = contents + pEmr->emr.nSize; printf(" iGraphicsMode: %u\n",pEmr->iGraphicsMode ); printf(" rclBounds: "); rectl_print(pEmr->rclBounds); printf("\n"); printf(" exScale: %f\n",pEmr->exScale ); printf(" eyScale: %f\n",pEmr->eyScale ); printf(" emrtext: "); - emrtext_print(contents + sizeof(U_EMREXTTEXTOUTA) - sizeof(U_EMRTEXT),contents,type); + emrtext_print(contents + sizeof(U_EMREXTTEXTOUTA) - sizeof(U_EMRTEXT),contents,blimit,type); printf("\n"); } @@ -724,6 +798,10 @@ void core8_print(const char *name, const char *contents, int type){ void core9_print(const char *name, const char *contents){ UNUSED(name); PU_EMRARC pEmr = (PU_EMRARC) (contents); + if(pEmr->emr.nSize < sizeof(U_EMRARC)){ + printf(" record corruption HERE\n"); + return; + } printf(" rclBox: "); rectl_print(pEmr->rclBox); printf("\n"); printf(" ptlStart: "); pointl_print(pEmr->ptlStart); printf("\n"); printf(" ptlEnd: "); pointl_print(pEmr->ptlEnd); printf("\n"); @@ -734,16 +812,23 @@ void core10_print(const char *name, const char *contents){ UNUSED(name); unsigned int i; PU_EMRPOLYPOLYLINE16 pEmr = (PU_EMRPOLYPOLYLINE16) (contents); + if(pEmr->emr.nSize < sizeof(U_EMRPOLYPOLYLINE16)){ + printf(" record corruption HERE\n"); + return; + } + const char *blimit = contents + pEmr->emr.nSize; printf(" rclBounds: "); rectl_print(pEmr->rclBounds); printf("\n"); printf(" nPolys: %d\n",pEmr->nPolys ); printf(" cpts: %d\n",pEmr->cpts ); printf(" Counts: "); + IF_MEM_UNSAFE_PRINT_AND_RETURN(&(pEmr->aPolyCounts), pEmr->nPolys*sizeof(U_POLYCOUNTS), blimit); for(i=0;i<pEmr->nPolys; i++){ printf(" [%d]:%d ",i,pEmr->aPolyCounts[i] ); } printf("\n"); printf(" Points: "); - PU_POINT16 papts = (PU_POINT16)((char *)pEmr->aPolyCounts + sizeof(uint32_t)* pEmr->nPolys); + PU_POINT16 papts = (PU_POINT16)((char *)pEmr->aPolyCounts + pEmr->nPolys*sizeof(U_POLYCOUNTS) ); + IF_MEM_UNSAFE_PRINT_AND_RETURN(papts, pEmr->cpts*sizeof(U_POINT16), blimit); for(i=0; i<pEmr->cpts; i++){ printf(" [%d]:",i); point16_print(papts[i]); } @@ -754,20 +839,18 @@ void core10_print(const char *name, const char *contents){ // Functions with the same form starting with U_EMRINVERTRGN_print and U_EMRPAINTRGN_print, void core11_print(const char *name, const char *contents){ UNUSED(name); - unsigned int i,roff; PU_EMRINVERTRGN pEmr = (PU_EMRINVERTRGN) (contents); + if(pEmr->emr.nSize < sizeof(U_EMRINVERTRGN)){ + printf(" record corruption HERE\n"); + return; + } + const char *blimit = contents + pEmr->emr.nSize; printf(" rclBounds: "); rectl_print(pEmr->rclBounds); printf("\n"); printf(" cbRgnData: %d\n",pEmr->cbRgnData); - // This one is a pain since each RGNDATA may be a different size, so it isn't possible to index through them. - roff=0; - i=1; - char *prd = (char *) &(pEmr->RgnData); - while(roff + sizeof(U_RGNDATAHEADER) < pEmr->cbRgnData){ // stop at end of the record 4*4 = header + 4*4=rect - printf(" RegionData:%d",i); - rgndata_print((PU_RGNDATA) (prd + roff)); - roff += (((PU_RGNDATA)prd)->rdh.dwSize + ((PU_RGNDATA)prd)->rdh.nRgnSize - 16); - printf("\n"); - } + printf(" RegionData:"); + const char *minptr = MAKE_MIN_PTR(((const char *) &pEmr->RgnData + pEmr->cbRgnData),blimit); + rgndata_print(pEmr->RgnData, minptr); + printf("\n"); } @@ -775,13 +858,18 @@ void core11_print(const char *name, const char *contents){ void core12_print(const char *name, const char *contents){ UNUSED(name); PU_EMRCREATEMONOBRUSH pEmr = (PU_EMRCREATEMONOBRUSH) (contents); + if(pEmr->emr.nSize < sizeof(U_EMRCREATEMONOBRUSH)){ + printf(" record corruption HERE\n"); + return; + } + const char *blimit = contents + pEmr->emr.nSize; printf(" ihBrush: %u\n",pEmr->ihBrush ); printf(" iUsage : %u\n",pEmr->iUsage ); printf(" offBmi : %u\n",pEmr->offBmi ); printf(" cbBmi : %u\n",pEmr->cbBmi ); if(pEmr->cbBmi){ printf(" bitmap:"); - bitmapinfo_print(contents + pEmr->offBmi); + bitmapinfo_print(contents + pEmr->offBmi, blimit); printf("\n"); } printf(" offBits: %u\n",pEmr->offBits ); @@ -792,6 +880,11 @@ void core12_print(const char *name, const char *contents){ void core13_print(const char *name, const char *contents){ UNUSED(name); PU_EMRALPHABLEND pEmr = (PU_EMRALPHABLEND) (contents); + if(pEmr->emr.nSize < sizeof(U_EMRALPHABLEND)){ + printf(" record corruption HERE\n"); + return; + } + const char *blimit = contents + pEmr->emr.nSize; printf(" rclBounds: "); rectl_print( pEmr->rclBounds); printf("\n"); printf(" Dest: "); pointl_print(pEmr->Dest); printf("\n"); printf(" cDest: "); pointl_print(pEmr->cDest); printf("\n"); @@ -804,7 +897,7 @@ void core13_print(const char *name, const char *contents){ printf(" cbBmiSrc: %u\n",pEmr->cbBmiSrc ); if(pEmr->cbBmiSrc){ printf(" bitmap:"); - bitmapinfo_print(contents + pEmr->offBmiSrc); + bitmapinfo_print(contents + pEmr->offBmiSrc, blimit); printf("\n"); } printf(" offBitsSrc: %u\n",pEmr->offBitsSrc ); @@ -839,6 +932,11 @@ void U_EMRHEADER_print(const char *contents){ int p1len; PU_EMRHEADER pEmr = (PU_EMRHEADER)(contents); + if(pEmr->emr.nSize < sizeof(U_EMRHEADER)){ + printf(" record corruption HERE\n"); + return; + } + const char *blimit = contents + pEmr->emr.nSize; printf(" rclBounds: "); rectl_print( pEmr->rclBounds); printf("\n"); printf(" rclFrame: "); rectl_print( pEmr->rclFrame); printf("\n"); printf(" dSignature: 0x%8.8X\n", pEmr->dSignature ); @@ -850,6 +948,7 @@ void U_EMRHEADER_print(const char *contents){ printf(" nDescription: %d\n", pEmr->nDescription ); printf(" offDescription: %d\n", pEmr->offDescription); if(pEmr->offDescription){ + IF_MEM_UNSAFE_PRINT_AND_RETURN(contents, pEmr->offDescription + pEmr->nDescription*2*sizeof(char), blimit); string = U_Utf16leToUtf8((uint16_t *)((char *) pEmr + pEmr->offDescription), pEmr->nDescription, NULL); printf(" Desc. A: %s\n",string); free(string); @@ -868,6 +967,7 @@ void U_EMRHEADER_print(const char *contents){ printf(" offPixelFormat: %d\n", pEmr->offPixelFormat); if(pEmr->cbPixelFormat){ printf(" PFD:"); + IF_MEM_UNSAFE_PRINT_AND_RETURN(contents, pEmr->offPixelFormat + sizeof(U_PIXELFORMATDESCRIPTOR), blimit); pixelformatdescriptor_print( *(PU_PIXELFORMATDESCRIPTOR) (contents + pEmr->offPixelFormat)); printf("\n"); } @@ -997,6 +1097,10 @@ void U_EMRSETBRUSHORGEX_print(const char *contents){ */ void U_EMREOF_print(const char *contents){ PU_EMREOF pEmr = (PU_EMREOF)(contents); + if(pEmr->emr.nSize < sizeof(U_EMREOF)){ + printf(" record corruption HERE\n"); + return; + } printf(" cbPalEntries: %u\n", pEmr->cbPalEntries ); printf(" offPalEntries: %u\n", pEmr->offPalEntries); if(pEmr->cbPalEntries){ @@ -1014,6 +1118,10 @@ void U_EMREOF_print(const char *contents){ */ void U_EMRSETPIXELV_print(const char *contents){ PU_EMRSETPIXELV pEmr = (PU_EMRSETPIXELV)(contents); + if(pEmr->emr.nSize < sizeof(U_EMRSETPIXELV)){ + printf(" record corruption HERE\n"); + return; + } printf(" ptlPixel: "); pointl_print( pEmr->ptlPixel); printf("\n"); printf(" crColor: "); colorref_print(pEmr->crColor); printf("\n"); } @@ -1026,6 +1134,10 @@ void U_EMRSETPIXELV_print(const char *contents){ */ void U_EMRSETMAPPERFLAGS_print(const char *contents){ PU_EMRSETMAPPERFLAGS pEmr = (PU_EMRSETMAPPERFLAGS)(contents); + if(pEmr->emr.nSize < sizeof(U_EMRSETMAPPERFLAGS)){ + printf(" record corruption HERE\n"); + return; + } printf(" dwFlags: 0x%8.8X\n",pEmr->dwFlags); } @@ -1091,6 +1203,10 @@ void U_EMRSETTEXTALIGN_print(const char *contents){ */ void U_EMRSETCOLORADJUSTMENT_print(const char *contents){ PU_EMRSETCOLORADJUSTMENT pEmr = (PU_EMRSETCOLORADJUSTMENT)(contents); + if(pEmr->emr.nSize < sizeof(U_EMRSETCOLORADJUSTMENT)){ + printf(" record corruption HERE\n"); + return; + } printf(" ColorAdjustment:"); coloradjustment_print(pEmr->ColorAdjustment); printf("\n"); @@ -1203,6 +1319,10 @@ void U_EMRRESTOREDC_print(const char *contents){ */ void U_EMRSETWORLDTRANSFORM_print(const char *contents){ PU_EMRSETWORLDTRANSFORM pEmr = (PU_EMRSETWORLDTRANSFORM)(contents); + if(pEmr->emr.nSize < sizeof(U_EMRSETWORLDTRANSFORM)){ + printf(" record corruption HERE\n"); + return; + } printf(" xform:"); xform_print(pEmr->xform); printf("\n"); @@ -1215,6 +1335,10 @@ void U_EMRSETWORLDTRANSFORM_print(const char *contents){ */ void U_EMRMODIFYWORLDTRANSFORM_print(const char *contents){ PU_EMRMODIFYWORLDTRANSFORM pEmr = (PU_EMRMODIFYWORLDTRANSFORM)(contents); + if(pEmr->emr.nSize < sizeof(U_EMRMODIFYWORLDTRANSFORM)){ + printf(" record corruption HERE\n"); + return; + } printf(" xform:"); xform_print(pEmr->xform); printf("\n"); @@ -1228,6 +1352,10 @@ void U_EMRMODIFYWORLDTRANSFORM_print(const char *contents){ */ void U_EMRSELECTOBJECT_print(const char *contents){ PU_EMRSELECTOBJECT pEmr = (PU_EMRSELECTOBJECT)(contents); + if(pEmr->emr.nSize < sizeof(U_EMRSELECTOBJECT)){ + printf(" record corruption HERE\n"); + return; + } if(pEmr->ihObject & U_STOCK_OBJECT){ printf(" StockObject: 0x%8.8X\n", pEmr->ihObject ); } @@ -1243,6 +1371,10 @@ void U_EMRSELECTOBJECT_print(const char *contents){ */ void U_EMRCREATEPEN_print(const char *contents){ PU_EMRCREATEPEN pEmr = (PU_EMRCREATEPEN)(contents); + if(pEmr->emr.nSize < sizeof(U_EMRCREATEPEN)){ + printf(" record corruption HERE\n"); + return; + } printf(" ihPen: %u\n", pEmr->ihPen ); printf(" lopn: "); logpen_print(pEmr->lopn); printf("\n"); } @@ -1254,6 +1386,10 @@ void U_EMRCREATEPEN_print(const char *contents){ */ void U_EMRCREATEBRUSHINDIRECT_print(const char *contents){ PU_EMRCREATEBRUSHINDIRECT pEmr = (PU_EMRCREATEBRUSHINDIRECT)(contents); + if(pEmr->emr.nSize < sizeof(U_EMRCREATEBRUSHINDIRECT)){ + printf(" record corruption HERE\n"); + return; + } printf(" ihBrush: %u\n", pEmr->ihBrush ); printf(" lb: "); logbrush_print(pEmr->lb); printf("\n"); } @@ -1265,6 +1401,10 @@ void U_EMRCREATEBRUSHINDIRECT_print(const char *contents){ */ void U_EMRDELETEOBJECT_print(const char *contents){ PU_EMRDELETEOBJECT pEmr = (PU_EMRDELETEOBJECT)(contents); + if(pEmr->emr.nSize < sizeof(U_EMRDELETEOBJECT)){ + printf(" record corruption HERE\n"); + return; + } printf(" ihObject: %u\n", pEmr->ihObject ); } @@ -1275,6 +1415,10 @@ void U_EMRDELETEOBJECT_print(const char *contents){ */ void U_EMRANGLEARC_print(const char *contents){ PU_EMRANGLEARC pEmr = (PU_EMRANGLEARC)(contents); + if(pEmr->emr.nSize < sizeof(U_EMRANGLEARC)){ + printf(" record corruption HERE\n"); + return; + } printf(" ptlCenter: "), pointl_print(pEmr->ptlCenter ); printf("\n"); printf(" nRadius: %u\n", pEmr->nRadius ); printf(" eStartAngle: %f\n", pEmr->eStartAngle ); @@ -1306,6 +1450,10 @@ void U_EMRRECTANGLE_print(const char *contents){ */ void U_EMRROUNDRECT_print(const char *contents){ PU_EMRROUNDRECT pEmr = (PU_EMRROUNDRECT)(contents); + if(pEmr->emr.nSize < sizeof(U_EMRROUNDRECT)){ + printf(" record corruption HERE\n"); + return; + } printf(" rclBox: "), rectl_print(pEmr->rclBox ); printf("\n"); printf(" szlCorner: "), sizel_print(pEmr->szlCorner ); printf("\n"); } @@ -1353,6 +1501,10 @@ void U_EMRSELECTPALETTE_print(const char *contents){ */ void U_EMRCREATEPALETTE_print(const char *contents){ PU_EMRCREATEPALETTE pEmr = (PU_EMRCREATEPALETTE)(contents); + if(pEmr->emr.nSize < sizeof(U_EMRCREATEPALETTE)){ + printf(" record corruption HERE\n"); + return; + } printf(" ihPal: %u\n",pEmr->ihPal); printf(" lgpl: "), logpalette_print( (PU_LOGPALETTE)&(pEmr->lgpl) ); printf("\n"); } @@ -1365,12 +1517,18 @@ void U_EMRCREATEPALETTE_print(const char *contents){ void U_EMRSETPALETTEENTRIES_print(const char *contents){ unsigned int i; PU_EMRSETPALETTEENTRIES pEmr = (PU_EMRSETPALETTEENTRIES)(contents); + if(pEmr->emr.nSize < sizeof(U_EMRSETPALETTEENTRIES)){ + printf(" record corruption HERE\n"); + return; + } + const char *blimit = contents + pEmr->emr.nSize; printf(" ihPal: %u\n",pEmr->ihPal); printf(" iStart: %u\n",pEmr->iStart); printf(" cEntries: %u\n",pEmr->cEntries); if(pEmr->cEntries){ printf(" PLTEntries:"); PU_LOGPLTNTRY aPalEntries = (PU_LOGPLTNTRY) &(pEmr->aPalEntries); + IF_MEM_UNSAFE_PRINT_AND_RETURN(aPalEntries, pEmr->cEntries*sizeof(U_LOGPLTNTRY), blimit); for(i=0; i<pEmr->cEntries; i++){ printf("%d:",i); logpltntry_print(aPalEntries[i]); } @@ -1403,6 +1561,10 @@ void U_EMRREALIZEPALETTE_print(const char *contents){ */ void U_EMREXTFLOODFILL_print(const char *contents){ PU_EMREXTFLOODFILL pEmr = (PU_EMREXTFLOODFILL)(contents); + if(pEmr->emr.nSize < sizeof(U_EMREXTFLOODFILL)){ + printf(" record corruption HERE\n"); + return; + } printf(" ptlStart: "); pointl_print(pEmr->ptlStart); printf("\n"); printf(" crColor: "); colorref_print(pEmr->crColor); printf("\n"); printf(" iMode: %u\n",pEmr->iMode); @@ -1434,17 +1596,25 @@ void U_EMRARCTO_print(const char *contents){ void U_EMRPOLYDRAW_print(const char *contents){ unsigned int i; PU_EMRPOLYDRAW pEmr = (PU_EMRPOLYDRAW)(contents); + if(pEmr->emr.nSize < sizeof(U_EMRPOLYDRAW)){ + printf(" record corruption HERE\n"); + return; + } + const char *blimit = contents + pEmr->emr.nSize; printf(" rclBounds: "); rectl_print( pEmr->rclBounds); printf("\n"); printf(" cptl: %d\n",pEmr->cptl ); printf(" Points: "); + IF_MEM_UNSAFE_PRINT_AND_RETURN(pEmr->aptl, pEmr->cptl*sizeof(U_POINTL), blimit); for(i=0;i<pEmr->cptl; i++){ printf(" [%d]:",i); pointl_print(pEmr->aptl[i]); } printf("\n"); printf(" Types: "); + const char *abTypes = (const char *) pEmr->aptl + pEmr->cptl*sizeof(U_POINTL); + IF_MEM_UNSAFE_PRINT_AND_RETURN(abTypes, pEmr->cptl, blimit); for(i=0;i<pEmr->cptl; i++){ - printf(" [%d]:%u ",i,pEmr->abTypes[i]); + printf(" [%d]:%u ",i,((uint8_t *)abTypes)[i]); } printf("\n"); } @@ -1565,12 +1735,11 @@ void U_EMRABORTPATH_print(const char *contents){ /** \brief Print a pointer to a U_EMR_COMMENT record. \param contents pointer to a location in memory holding the comment record - \param blimit size in bytes of the comment record - \param off offset in bytes to the first byte in this record + \param off offset in bytes to the first byte in this record (needed for EMF+ record printing) EMF+ records, if any, are stored in EMF comment records. */ -void U_EMRCOMMENT_print(const char *contents, const char *blimit, size_t off){ +void U_EMRCOMMENT_print(const char *contents, size_t off){ char *string; char *src; uint32_t cIdent,cIdent2,cbData; @@ -1579,21 +1748,30 @@ void U_EMRCOMMENT_print(const char *contents, const char *blimit, size_t off){ static int recnum=0; PU_EMRCOMMENT pEmr = (PU_EMRCOMMENT)(contents); + if(pEmr->emr.nSize < sizeof(U_EMRCOMMENT)){ + printf(" record corruption HERE\n"); + return; + } + const char *blimit = contents + pEmr->emr.nSize; /* There are several different types of comments */ + IF_MEM_UNSAFE_PRINT_AND_RETURN(contents, sizeof(U_EMRCOMMENT), blimit); cbData = pEmr->cbData; printf(" cbData: %d\n",cbData ); + IF_MEM_UNSAFE_PRINT_AND_RETURN(contents, sizeof(U_EMR) + sizeof(U_CBDATA) + cbData, blimit); src = (char *)&(pEmr->Data); // default if(cbData >= 4){ /* Since the comment is just a big bag of bytes the emf endian code cannot safely touch - any of its payload. This is the only record type with that limitation. Try to determine + any of its payload. This is the only record type with that limitation. So the record + may appear at this stage with the wrong endianness. Try to determine what the contents are even if more byte swapping is required. */ cIdent = *(uint32_t *)(src); if(U_BYTE_SWAP){ U_swap4(&(cIdent),1); } if( cIdent == U_EMR_COMMENT_PUBLIC ){ printf(" cIdent: Public\n"); PU_EMRCOMMENT_PUBLIC pEmrp = (PU_EMRCOMMENT_PUBLIC) pEmr; + IF_MEM_UNSAFE_PRINT_AND_RETURN(contents, sizeof(U_EMRCOMMENT_PUBLIC), blimit); cIdent2 = pEmrp->pcIdent; if(U_BYTE_SWAP){ U_swap4(&(cIdent2),1); } printf(" pcIdent: 0x%8.8x\n",cIdent2); @@ -1603,6 +1781,7 @@ void U_EMRCOMMENT_print(const char *contents, const char *blimit, size_t off){ else if(cIdent == U_EMR_COMMENT_SPOOL ){ printf(" cIdent: Spool\n"); PU_EMRCOMMENT_SPOOL pEmrs = (PU_EMRCOMMENT_SPOOL) pEmr; + IF_MEM_UNSAFE_PRINT_AND_RETURN(contents, sizeof(U_EMRCOMMENT_SPOOL), blimit); cIdent2 = pEmrs->esrIdent; if(U_BYTE_SWAP){ U_swap4(&(cIdent2),1); } printf(" esrIdent: 0x%8.8x\n",cIdent2); @@ -1612,11 +1791,16 @@ void U_EMRCOMMENT_print(const char *contents, const char *blimit, size_t off){ else if(cIdent == U_EMR_COMMENT_EMFPLUSRECORD){ printf(" cIdent: EMF+\n"); PU_EMRCOMMENT_EMFPLUS pEmrpl = (PU_EMRCOMMENT_EMFPLUS) pEmr; + IF_MEM_UNSAFE_PRINT_AND_RETURN(contents, sizeof(U_EMRCOMMENT_EMFPLUS), blimit); src = (char *)&(pEmrpl->Data); loff = 16; /* Header size of the header part of an EMF+ comment record */ while(loff < cbData + 12){ // EMF+ records may not fill the entire comment, cbData value includes cIdent, but not U_EMR or cbData recsize = U_pmf_onerec_print(src, blimit, recnum, loff + off); - if(recsize<=0)break; + if(recsize==0){ break; } + else if(recsize<0){ + printf(" record corruption HERE\n"); + return; + } loff += recsize; src += recsize; recnum++; @@ -1642,19 +1826,17 @@ void U_EMRCOMMENT_print(const char *contents, const char *blimit, size_t off){ \param contents pointer to a buffer holding all EMR records */ void U_EMRFILLRGN_print(const char *contents){ - int i,roff; PU_EMRFILLRGN pEmr = (PU_EMRFILLRGN)(contents); + if(pEmr->emr.nSize < sizeof(U_EMRFILLRGN)){ + printf(" record corruption HERE\n"); + return; + } + const char *blimit = contents + pEmr->emr.nSize; printf(" rclBounds: "); rectl_print(pEmr->rclBounds); printf("\n"); printf(" cbRgnData: %u\n",pEmr->cbRgnData); printf(" ihBrush: %u\n",pEmr->ihBrush); - // This one is a pain since each RGNDATA may be a different size, so it isn't possible to index through them. - roff=0; - i=1; - char *prd = (char *) &(pEmr->RgnData); - while(roff + sizeof(U_RGNDATAHEADER) < pEmr->emr.nSize){ // up to the end of the record - printf(" RegionData[%d]: ",i); rgndata_print((PU_RGNDATA) (prd + roff)); printf("\n"); - roff += (((PU_RGNDATA)prd)->rdh.dwSize + ((PU_RGNDATA)prd)->rdh.nRgnSize - 16); - } + const char *minptr = MAKE_MIN_PTR(((const char *) &pEmr->RgnData + pEmr->cbRgnData + sizeof(U_RGNDATAHEADER)),blimit); + printf(" RegionData: "); rgndata_print(pEmr->RgnData, minptr); printf("\n"); } // U_EMRFRAMERGN 72 @@ -1663,20 +1845,18 @@ void U_EMRFILLRGN_print(const char *contents){ \param contents pointer to a buffer holding all EMR records */ void U_EMRFRAMERGN_print(const char *contents){ - int i,roff; PU_EMRFRAMERGN pEmr = (PU_EMRFRAMERGN)(contents); + if(pEmr->emr.nSize < sizeof(U_EMRFRAMERGN)){ + printf(" record corruption HERE\n"); + return; + } + const char *blimit = contents + pEmr->emr.nSize; printf(" rclBounds: "); rectl_print(pEmr->rclBounds); printf("\n"); printf(" cbRgnData: %u\n",pEmr->cbRgnData); printf(" ihBrush: %u\n",pEmr->ihBrush); printf(" szlStroke: "), sizel_print(pEmr->szlStroke ); printf("\n"); - // This one is a pain since each RGNDATA may be a different size, so it isn't possible to index through them. - roff=0; - i=1; - char *prd = (char *) &(pEmr->RgnData); - while(roff + sizeof(U_RGNDATAHEADER) < pEmr->emr.nSize){ // up to the end of the record - printf(" RegionData[%d]: ",i); rgndata_print((PU_RGNDATA) (prd + roff)); printf("\n"); - roff += (((PU_RGNDATA)prd)->rdh.dwSize + ((PU_RGNDATA)prd)->rdh.nRgnSize - 16); - } + const char *minptr = MAKE_MIN_PTR(((const char *) &pEmr->RgnData + pEmr->cbRgnData),blimit); + printf(" RegionData: "); rgndata_print(pEmr->RgnData, minptr); printf("\n"); } // U_EMRINVERTRGN 73 @@ -1703,17 +1883,16 @@ void U_EMRPAINTRGN_print(const char *contents){ \param contents pointer to a buffer holding all EMR records */ void U_EMREXTSELECTCLIPRGN_print(const char *contents){ - int i,roff; PU_EMREXTSELECTCLIPRGN pEmr = (PU_EMREXTSELECTCLIPRGN) (contents); + if(pEmr->emr.nSize < sizeof(U_EMREXTSELECTCLIPRGN)){ + printf(" record corruption HERE\n"); + return; + } + const char *blimit = contents + pEmr->emr.nSize; printf(" cbRgnData: %u\n",pEmr->cbRgnData); printf(" iMode: %u\n",pEmr->iMode); - // This one is a pain since each RGNDATA may be a different size, so it isn't possible to index through them. - char *prd = (char *) &(pEmr->RgnData); - i=roff=0; - while(roff + sizeof(U_RGNDATAHEADER) < pEmr->cbRgnData){ // stop at end of the record 4*4 = header + 4*4=rect - printf(" RegionData[%d]: ",i++); rgndata_print((PU_RGNDATA) (prd + roff)); printf("\n"); - roff += (((PU_RGNDATA)prd)->rdh.dwSize + ((PU_RGNDATA)prd)->rdh.nRgnSize - 16); - } + const char *minptr = MAKE_MIN_PTR(((const char *) &pEmr->RgnData + pEmr->cbRgnData),blimit); + printf(" RegionData: "); rgndata_print(pEmr->RgnData, minptr); printf("\n"); } // U_EMRBITBLT 76 @@ -1723,6 +1902,11 @@ void U_EMREXTSELECTCLIPRGN_print(const char *contents){ */ void U_EMRBITBLT_print(const char *contents){ PU_EMRBITBLT pEmr = (PU_EMRBITBLT) (contents); + if(pEmr->emr.nSize < sizeof(U_EMRBITBLT)){ + printf(" record corruption HERE\n"); + return; + } + const char *blimit = contents + pEmr->emr.nSize; printf(" rclBounds: "); rectl_print( pEmr->rclBounds); printf("\n"); printf(" Dest: "); pointl_print(pEmr->Dest); printf("\n"); printf(" cDest: "); pointl_print(pEmr->cDest); printf("\n"); @@ -1735,7 +1919,7 @@ void U_EMRBITBLT_print(const char *contents){ printf(" cbBmiSrc: %u\n", pEmr->cbBmiSrc ); if(pEmr->cbBmiSrc){ printf(" bitmap: "); - bitmapinfo_print(contents + pEmr->offBmiSrc); + bitmapinfo_print(contents + pEmr->offBmiSrc, blimit); printf("\n"); } printf(" offBitsSrc: %u\n", pEmr->offBitsSrc ); @@ -1749,6 +1933,11 @@ void U_EMRBITBLT_print(const char *contents){ */ void U_EMRSTRETCHBLT_print(const char *contents){ PU_EMRSTRETCHBLT pEmr = (PU_EMRSTRETCHBLT) (contents); + if(pEmr->emr.nSize < sizeof(U_EMRSTRETCHBLT)){ + printf(" record corruption HERE\n"); + return; + } + const char *blimit = contents + pEmr->emr.nSize; printf(" rclBounds: "); rectl_print( pEmr->rclBounds); printf("\n"); printf(" Dest: "); pointl_print(pEmr->Dest); printf("\n"); printf(" cDest: "); pointl_print(pEmr->cDest); printf("\n"); @@ -1761,7 +1950,7 @@ void U_EMRSTRETCHBLT_print(const char *contents){ printf(" cbBmiSrc: %u\n", pEmr->cbBmiSrc ); if(pEmr->cbBmiSrc){ printf(" bitmap: "); - bitmapinfo_print(contents + pEmr->offBmiSrc); + bitmapinfo_print(contents + pEmr->offBmiSrc, blimit); printf("\n"); } printf(" offBitsSrc: %u\n", pEmr->offBitsSrc ); @@ -1776,6 +1965,11 @@ void U_EMRSTRETCHBLT_print(const char *contents){ */ void U_EMRMASKBLT_print(const char *contents){ PU_EMRMASKBLT pEmr = (PU_EMRMASKBLT) (contents); + if(pEmr->emr.nSize < sizeof(U_EMRMASKBLT)){ + printf(" record corruption HERE\n"); + return; + } + const char *blimit = contents + pEmr->emr.nSize; printf(" rclBounds: "); rectl_print( pEmr->rclBounds); printf("\n"); printf(" Dest: "); pointl_print(pEmr->Dest); printf("\n"); printf(" cDest: "); pointl_print(pEmr->cDest); printf("\n"); @@ -1788,7 +1982,7 @@ void U_EMRMASKBLT_print(const char *contents){ printf(" cbBmiSrc: %u\n", pEmr->cbBmiSrc ); if(pEmr->cbBmiSrc){ printf(" Src bitmap: "); - bitmapinfo_print(contents + pEmr->offBmiSrc); + bitmapinfo_print(contents + pEmr->offBmiSrc, blimit); printf("\n"); } printf(" offBitsSrc: %u\n", pEmr->offBitsSrc ); @@ -1799,7 +1993,7 @@ void U_EMRMASKBLT_print(const char *contents){ printf(" cbBmiMask: %u\n", pEmr->cbBmiMask ); if(pEmr->cbBmiMask){ printf(" Mask bitmap: "); - bitmapinfo_print(contents + pEmr->offBmiMask); + bitmapinfo_print(contents + pEmr->offBmiMask, blimit); printf("\n"); } printf(" offBitsMask: %u\n", pEmr->offBitsMask ); @@ -1813,6 +2007,11 @@ void U_EMRMASKBLT_print(const char *contents){ */ void U_EMRPLGBLT_print(const char *contents){ PU_EMRPLGBLT pEmr = (PU_EMRPLGBLT) (contents); + if(pEmr->emr.nSize < sizeof(U_EMRPLGBLT)){ + printf(" record corruption HERE\n"); + return; + } + const char *blimit = contents + pEmr->emr.nSize; printf(" rclBounds: "); rectl_print( pEmr->rclBounds); printf("\n"); printf(" aptlDst(UL): "); pointl_print(pEmr->aptlDst[0]); printf("\n"); printf(" aptlDst(UR): "); pointl_print(pEmr->aptlDst[1]); printf("\n"); @@ -1826,7 +2025,7 @@ void U_EMRPLGBLT_print(const char *contents){ printf(" cbBmiSrc: %u\n", pEmr->cbBmiSrc ); if(pEmr->cbBmiSrc){ printf(" Src bitmap: "); - bitmapinfo_print(contents + pEmr->offBmiSrc); + bitmapinfo_print(contents + pEmr->offBmiSrc, blimit); printf("\n"); } printf(" offBitsSrc: %u\n", pEmr->offBitsSrc ); @@ -1837,7 +2036,7 @@ void U_EMRPLGBLT_print(const char *contents){ printf(" cbBmiMask: %u\n", pEmr->cbBmiMask ); if(pEmr->cbBmiMask){ printf(" Mask bitmap: "); - bitmapinfo_print(contents + pEmr->offBmiMask); + bitmapinfo_print(contents + pEmr->offBmiMask, blimit); printf("\n"); } printf(" offBitsMask: %u\n", pEmr->offBitsMask ); @@ -1851,6 +2050,11 @@ void U_EMRPLGBLT_print(const char *contents){ */ void U_EMRSETDIBITSTODEVICE_print(const char *contents){ PU_EMRSETDIBITSTODEVICE pEmr = (PU_EMRSETDIBITSTODEVICE) (contents); + if(pEmr->emr.nSize < sizeof(U_EMRSETDIBITSTODEVICE)){ + printf(" record corruption HERE\n"); + return; + } + const char *blimit = contents + pEmr->emr.nSize; printf(" rclBounds: "); rectl_print( pEmr->rclBounds); printf("\n"); printf(" Dest: "); pointl_print(pEmr->Dest); printf("\n"); printf(" Src: "); pointl_print(pEmr->Src); printf("\n"); @@ -1859,7 +2063,7 @@ void U_EMRSETDIBITSTODEVICE_print(const char *contents){ printf(" cbBmiSrc: %u\n", pEmr->cbBmiSrc ); if(pEmr->cbBmiSrc){ printf(" Src bitmap: "); - bitmapinfo_print(contents + pEmr->offBmiSrc); + bitmapinfo_print(contents + pEmr->offBmiSrc, blimit); printf("\n"); } printf(" offBitsSrc: %u\n", pEmr->offBitsSrc ); @@ -1876,6 +2080,11 @@ void U_EMRSETDIBITSTODEVICE_print(const char *contents){ */ void U_EMRSTRETCHDIBITS_print(const char *contents){ PU_EMRSTRETCHDIBITS pEmr = (PU_EMRSTRETCHDIBITS) (contents); + if(pEmr->emr.nSize < sizeof(U_EMRSTRETCHDIBITS)){ + printf(" record corruption HERE\n"); + return; + } + const char *blimit = contents + pEmr->emr.nSize; printf(" rclBounds: "); rectl_print( pEmr->rclBounds); printf("\n"); printf(" Dest: "); pointl_print(pEmr->Dest); printf("\n"); printf(" Src: "); pointl_print(pEmr->Src); printf("\n"); @@ -1884,7 +2093,7 @@ void U_EMRSTRETCHDIBITS_print(const char *contents){ printf(" cbBmiSrc: %u\n", pEmr->cbBmiSrc ); if(pEmr->cbBmiSrc){ printf(" Src bitmap: "); - bitmapinfo_print(contents + pEmr->offBmiSrc); + bitmapinfo_print(contents + pEmr->offBmiSrc, blimit); printf("\n"); } printf(" offBitsSrc: %u\n", pEmr->offBitsSrc ); @@ -1901,12 +2110,19 @@ void U_EMRSTRETCHDIBITS_print(const char *contents){ */ void U_EMREXTCREATEFONTINDIRECTW_print(const char *contents){ PU_EMREXTCREATEFONTINDIRECTW pEmr = (PU_EMREXTCREATEFONTINDIRECTW) (contents); + if(pEmr->emr.nSize < U_SIZE_EMREXTCREATEFONTINDIRECTW_LOGFONT){ // smallest variant + printf(" record corruption HERE\n"); + return; + } + const char *blimit = contents + pEmr->emr.nSize; printf(" ihFont: %u\n",pEmr->ihFont ); printf(" Font: "); - if(pEmr->emr.nSize == sizeof(U_EMREXTCREATEFONTINDIRECTW)){ // holds logfont_panose + if(pEmr->emr.nSize == U_SIZE_EMREXTCREATEFONTINDIRECTW_LOGFONT_PANOSE){ // holds logfont_panose + IF_MEM_UNSAFE_PRINT_AND_RETURN(&(pEmr->elfw), sizeof(U_PANOSE), blimit); logfont_panose_print(pEmr->elfw); } - else { // holds logfont + else { // holds logfont or logfontExDv. The latter isn't supported but it starts with logfont, so use that + IF_MEM_UNSAFE_PRINT_AND_RETURN(&(pEmr->elfw), sizeof(U_LOGFONT), blimit); logfont_print( *(PU_LOGFONT) &(pEmr->elfw)); } printf("\n"); @@ -2002,17 +2218,25 @@ void U_EMRPOLYPOLYGON16_print(const char *contents){ void U_EMRPOLYDRAW16_print(const char *contents){ unsigned int i; PU_EMRPOLYDRAW16 pEmr = (PU_EMRPOLYDRAW16)(contents); + if(pEmr->emr.nSize < sizeof(U_EMRPOLYDRAW16)){ + printf(" record corruption HERE\n"); + return; + } + const char *blimit = contents + pEmr->emr.nSize; printf(" rclBounds: "); rectl_print( pEmr->rclBounds); printf("\n"); printf(" cpts: %d\n",pEmr->cpts ); printf(" Points: "); + IF_MEM_UNSAFE_PRINT_AND_RETURN(pEmr->apts, pEmr->cpts*sizeof(U_POINT16), blimit); for(i=0;i<pEmr->cpts; i++){ printf(" [%d]:",i); point16_print(pEmr->apts[i]); } printf("\n"); printf(" Types: "); + const char *abTypes = (const char *) pEmr->apts + pEmr->cpts*sizeof(U_POINT16); + IF_MEM_UNSAFE_PRINT_AND_RETURN(abTypes, pEmr->cpts, blimit); for(i=0;i<pEmr->cpts; i++){ - printf(" [%d]:%u ",i,pEmr->abTypes[i]); + printf(" [%d]:%u ",i,((uint8_t *)abTypes)[i]); } printf("\n"); } @@ -2043,12 +2267,17 @@ void U_EMRCREATEDIBPATTERNBRUSHPT_print(const char *contents){ */ void U_EMREXTCREATEPEN_print(const char *contents){ PU_EMREXTCREATEPEN pEmr = (PU_EMREXTCREATEPEN)(contents); + if(pEmr->emr.nSize < sizeof(U_EMREXTCREATEPEN)){ + printf(" record corruption HERE\n"); + return; + } + const char *blimit = contents + pEmr->emr.nSize; printf(" ihPen: %u\n", pEmr->ihPen ); printf(" offBmi: %u\n", pEmr->offBmi ); printf(" cbBmi: %u\n", pEmr->cbBmi ); if(pEmr->cbBmi){ printf(" bitmap: "); - bitmapinfo_print(contents + pEmr->offBmi); + bitmapinfo_print(contents + pEmr->offBmi, blimit); printf("\n"); } printf(" offBits: %u\n", pEmr->offBits ); @@ -2077,6 +2306,10 @@ void U_EMRSETICMMODE_print(const char *contents){ */ void U_EMRCREATECOLORSPACE_print(const char *contents){ PU_EMRCREATECOLORSPACE pEmr = (PU_EMRCREATECOLORSPACE)(contents); + if(pEmr->emr.nSize < sizeof(U_EMRCREATECOLORSPACE)){ + printf(" record corruption HERE\n"); + return; + } printf(" ihCS: %u\n", pEmr->ihCS ); printf(" ColorSpace: "); logcolorspacea_print(pEmr->lcs); printf("\n"); } @@ -2111,6 +2344,12 @@ void U_EMRDELETECOLORSPACE_print(const char *contents){ */ void U_EMRPIXELFORMAT_print(const char *contents){ PU_EMRPIXELFORMAT pEmr = (PU_EMRPIXELFORMAT)(contents); + if(pEmr->emr.nSize < sizeof(U_EMRPIXELFORMAT)){ + printf(" record corruption HERE\n"); + return; + } + const char *blimit = contents + pEmr->emr.nSize; + IF_MEM_UNSAFE_PRINT_AND_RETURN(&(pEmr->pfd), sizeof(U_PIXELFORMATDESCRIPTOR), blimit); printf(" Pfd: "); pixelformatdescriptor_print(pEmr->pfd); printf("\n"); } @@ -2130,6 +2369,11 @@ void U_EMRSMALLTEXTOUT_print(const char *contents){ int roff; char *string; PU_EMRSMALLTEXTOUT pEmr = (PU_EMRSMALLTEXTOUT)(contents); + if(pEmr->emr.nSize < sizeof(U_EMRSMALLTEXTOUT)){ + printf(" record corruption HERE\n"); + return; + } + const char *blimit = contents + pEmr->emr.nSize; printf(" Dest: "); pointl_print(pEmr->Dest); printf("\n"); printf(" cChars: %u\n", pEmr->cChars ); printf(" fuOptions: 0x%8.8X\n", pEmr->fuOptions ); @@ -2138,14 +2382,17 @@ void U_EMRSMALLTEXTOUT_print(const char *contents){ printf(" eyScale: %f\n", pEmr->eyScale ); roff = sizeof(U_EMRSMALLTEXTOUT); //offset to the start of the variable fields if(!(pEmr->fuOptions & U_ETO_NO_RECT)){ + IF_MEM_UNSAFE_PRINT_AND_RETURN(contents, roff, blimit); printf(" rclBounds: "); rectl_print( *(PU_RECTL) (contents + roff)); printf("\n"); roff += sizeof(U_RECTL); } if(pEmr->fuOptions & U_ETO_SMALL_CHARS){ + IF_MEM_UNSAFE_PRINT_AND_RETURN(contents, roff + pEmr->cChars*sizeof(char), blimit); printf(" Text8: <%.*s>\n",pEmr->cChars,contents+roff); /* May not be null terminated */ } else { string = U_Utf16leToUtf8((uint16_t *)(contents+roff), pEmr->cChars, NULL); + IF_MEM_UNSAFE_PRINT_AND_RETURN(contents, roff + pEmr->cChars*2*sizeof(char), blimit); printf(" Text16: <%s>\n",contents+roff); free(string); } @@ -2199,12 +2446,18 @@ void U_EMRTRANSPARENTBLT_print(const char *contents){ void U_EMRGRADIENTFILL_print(const char *contents){ unsigned int i; PU_EMRGRADIENTFILL pEmr = (PU_EMRGRADIENTFILL)(contents); + if(pEmr->emr.nSize < sizeof(U_EMRGRADIENTFILL)){ + printf(" record corruption HERE\n"); + return; + } + const char *blimit = contents + pEmr->emr.nSize; printf(" rclBounds: "); rectl_print( pEmr->rclBounds); printf("\n"); printf(" nTriVert: %u\n", pEmr->nTriVert ); printf(" nGradObj: %u\n", pEmr->nGradObj ); printf(" ulMode: %u\n", pEmr->ulMode ); contents += sizeof(U_EMRGRADIENTFILL); if(pEmr->nTriVert){ + IF_MEM_UNSAFE_PRINT_AND_RETURN(contents, pEmr->nTriVert*sizeof(U_TRIVERTEX), blimit); printf(" TriVert: "); for(i=0; i<pEmr->nTriVert; i++, contents+=sizeof(U_TRIVERTEX)){ trivertex_print(*(PU_TRIVERTEX)(contents)); @@ -2214,12 +2467,14 @@ void U_EMRGRADIENTFILL_print(const char *contents){ if(pEmr->nGradObj){ printf(" GradObj: "); if( pEmr->ulMode == U_GRADIENT_FILL_TRIANGLE){ + IF_MEM_UNSAFE_PRINT_AND_RETURN(contents, pEmr->nGradObj*sizeof(U_GRADIENT3), blimit); for(i=0; i<pEmr->nGradObj; i++, contents+=sizeof(U_GRADIENT3)){ gradient3_print(*(PU_GRADIENT3)(contents)); } } else if(pEmr->ulMode == U_GRADIENT_FILL_RECT_H || pEmr->ulMode == U_GRADIENT_FILL_RECT_V){ + IF_MEM_UNSAFE_PRINT_AND_RETURN(contents, pEmr->nGradObj*sizeof(U_GRADIENT4), blimit); for(i=0; i<pEmr->nGradObj; i++, contents+=sizeof(U_GRADIENT4)){ gradient4_print(*(PU_GRADIENT4)(contents)); } @@ -2244,12 +2499,18 @@ void U_EMRGRADIENTFILL_print(const char *contents){ void U_EMRCREATECOLORSPACEW_print(const char *contents){ unsigned int i; PU_EMRCREATECOLORSPACEW pEmr = (PU_EMRCREATECOLORSPACEW)(contents); + if(pEmr->emr.nSize < sizeof(U_EMRCREATECOLORSPACEW)){ + printf(" record corruption HERE\n"); + return; + } + const char *blimit = contents + pEmr->emr.nSize; printf(" ihCS: %u\n", pEmr->ihCS ); printf(" ColorSpace: "); logcolorspacew_print(pEmr->lcs); printf("\n"); printf(" dwFlags: 0x%8.8X\n", pEmr->dwFlags ); printf(" cbData: %u\n", pEmr->cbData ); printf(" Data(hexvalues):"); if(pEmr->dwFlags & 1){ + IF_MEM_UNSAFE_PRINT_AND_RETURN(contents, pEmr->cbData, blimit); for(i=0; i<pEmr->cbData; i++){ printf("[%d]:%2.2X ",i,pEmr->Data[i]); } @@ -2267,145 +2528,168 @@ void U_EMRCREATECOLORSPACEW_print(const char *contents){ */ int U_emf_onerec_print(const char *contents, const char *blimit, int recnum, size_t off){ PU_ENHMETARECORD lpEMFR = (PU_ENHMETARECORD)(contents + off); - unsigned int size; + uint32_t nSize; + uint32_t iType; + const char *record = contents + off; - printf("%-30srecord:%5d type:%-4d offset:%8d rsize:%8d\n",U_emr_names(lpEMFR->iType),recnum,lpEMFR->iType,(int) off,lpEMFR->nSize); - size = lpEMFR->nSize; - contents += off; + if(record < contents)return(-1); // offset wrapped + + /* Check that COMMON data in record can be touched without an access violation. If it cannot be + this is either a corrupt EMF or one engineered to cause a buffer overflow. Pointer math + could wrap so check both sides of the range. + */ + if(!U_emf_record_sizeok(record, blimit, &nSize, &iType, 1))return(-1); + + uint32_t crc; +#if U_BYTE_SWAP + //This is a Big Endian machine, EMF crc values must be calculated on Little Endian form + char *swapbuf=malloc(nSize); + if(!swapbuf)return(-1); + memcpy(swapbuf,record,nSize); + U_emf_endian(swapbuf,nSize,1); // BE to LE + crc=lu_crc32(swapbuf,nSize); + free(swapbuf); +#else + crc=lu_crc32(record,nSize); +#endif + printf("%-30srecord:%5d type:%-4d offset:%8d rsize:%8d crc32:%8.8X\n", + U_emr_names(iType),recnum,iType,(int) off,nSize,crc); - /* Check that the record size is OK, abort if not. - Pointer math might wrap, so check both sides of the range */ - if(size < sizeof(U_EMR) || - contents + size - 1 >= blimit || - contents + size - 1 < contents)return(-1); + fflush(stdout); + /* print the record header before checking further. + Note if this is a corrupt record, but continue anyway. + The _print routines will stop at the actual problem and print another corrupt message. + */ + if(!U_emf_record_safe(record)){printf("WARNING: Corrupt record. Emitting fields above the problem.\n");} + switch (lpEMFR->iType) { - case U_EMR_HEADER: U_EMRHEADER_print(contents); break; - case U_EMR_POLYBEZIER: U_EMRPOLYBEZIER_print(contents); break; - case U_EMR_POLYGON: U_EMRPOLYGON_print(contents); break; - case U_EMR_POLYLINE: U_EMRPOLYLINE_print(contents); break; - case U_EMR_POLYBEZIERTO: U_EMRPOLYBEZIERTO_print(contents); break; - case U_EMR_POLYLINETO: U_EMRPOLYLINETO_print(contents); break; - case U_EMR_POLYPOLYLINE: U_EMRPOLYPOLYLINE_print(contents); break; - case U_EMR_POLYPOLYGON: U_EMRPOLYPOLYGON_print(contents); break; - case U_EMR_SETWINDOWEXTEX: U_EMRSETWINDOWEXTEX_print(contents); break; - case U_EMR_SETWINDOWORGEX: U_EMRSETWINDOWORGEX_print(contents); break; - case U_EMR_SETVIEWPORTEXTEX: U_EMRSETVIEWPORTEXTEX_print(contents); break; - case U_EMR_SETVIEWPORTORGEX: U_EMRSETVIEWPORTORGEX_print(contents); break; - case U_EMR_SETBRUSHORGEX: U_EMRSETBRUSHORGEX_print(contents); break; - case U_EMR_EOF: U_EMREOF_print(contents); size=0; break; - case U_EMR_SETPIXELV: U_EMRSETPIXELV_print(contents); break; - case U_EMR_SETMAPPERFLAGS: U_EMRSETMAPPERFLAGS_print(contents); break; - case U_EMR_SETMAPMODE: U_EMRSETMAPMODE_print(contents); break; - case U_EMR_SETBKMODE: U_EMRSETBKMODE_print(contents); break; - case U_EMR_SETPOLYFILLMODE: U_EMRSETPOLYFILLMODE_print(contents); break; - case U_EMR_SETROP2: U_EMRSETROP2_print(contents); break; - case U_EMR_SETSTRETCHBLTMODE: U_EMRSETSTRETCHBLTMODE_print(contents); break; - case U_EMR_SETTEXTALIGN: U_EMRSETTEXTALIGN_print(contents); break; - case U_EMR_SETCOLORADJUSTMENT: U_EMRSETCOLORADJUSTMENT_print(contents); break; - case U_EMR_SETTEXTCOLOR: U_EMRSETTEXTCOLOR_print(contents); break; - case U_EMR_SETBKCOLOR: U_EMRSETBKCOLOR_print(contents); break; - case U_EMR_OFFSETCLIPRGN: U_EMROFFSETCLIPRGN_print(contents); break; - case U_EMR_MOVETOEX: U_EMRMOVETOEX_print(contents); break; - case U_EMR_SETMETARGN: U_EMRSETMETARGN_print(contents); break; - case U_EMR_EXCLUDECLIPRECT: U_EMREXCLUDECLIPRECT_print(contents); break; - case U_EMR_INTERSECTCLIPRECT: U_EMRINTERSECTCLIPRECT_print(contents); break; - case U_EMR_SCALEVIEWPORTEXTEX: U_EMRSCALEVIEWPORTEXTEX_print(contents); break; - case U_EMR_SCALEWINDOWEXTEX: U_EMRSCALEWINDOWEXTEX_print(contents); break; - case U_EMR_SAVEDC: U_EMRSAVEDC_print(contents); break; - case U_EMR_RESTOREDC: U_EMRRESTOREDC_print(contents); break; - case U_EMR_SETWORLDTRANSFORM: U_EMRSETWORLDTRANSFORM_print(contents); break; - case U_EMR_MODIFYWORLDTRANSFORM: U_EMRMODIFYWORLDTRANSFORM_print(contents); break; - case U_EMR_SELECTOBJECT: U_EMRSELECTOBJECT_print(contents); break; - case U_EMR_CREATEPEN: U_EMRCREATEPEN_print(contents); break; - case U_EMR_CREATEBRUSHINDIRECT: U_EMRCREATEBRUSHINDIRECT_print(contents); break; - case U_EMR_DELETEOBJECT: U_EMRDELETEOBJECT_print(contents); break; - case U_EMR_ANGLEARC: U_EMRANGLEARC_print(contents); break; - case U_EMR_ELLIPSE: U_EMRELLIPSE_print(contents); break; - case U_EMR_RECTANGLE: U_EMRRECTANGLE_print(contents); break; - case U_EMR_ROUNDRECT: U_EMRROUNDRECT_print(contents); break; - case U_EMR_ARC: U_EMRARC_print(contents); break; - case U_EMR_CHORD: U_EMRCHORD_print(contents); break; - case U_EMR_PIE: U_EMRPIE_print(contents); break; - case U_EMR_SELECTPALETTE: U_EMRSELECTPALETTE_print(contents); break; - case U_EMR_CREATEPALETTE: U_EMRCREATEPALETTE_print(contents); break; - case U_EMR_SETPALETTEENTRIES: U_EMRSETPALETTEENTRIES_print(contents); break; - case U_EMR_RESIZEPALETTE: U_EMRRESIZEPALETTE_print(contents); break; - case U_EMR_REALIZEPALETTE: U_EMRREALIZEPALETTE_print(contents); break; - case U_EMR_EXTFLOODFILL: U_EMREXTFLOODFILL_print(contents); break; - case U_EMR_LINETO: U_EMRLINETO_print(contents); break; - case U_EMR_ARCTO: U_EMRARCTO_print(contents); break; - case U_EMR_POLYDRAW: U_EMRPOLYDRAW_print(contents); break; - case U_EMR_SETARCDIRECTION: U_EMRSETARCDIRECTION_print(contents); break; - case U_EMR_SETMITERLIMIT: U_EMRSETMITERLIMIT_print(contents); break; - case U_EMR_BEGINPATH: U_EMRBEGINPATH_print(contents); break; - case U_EMR_ENDPATH: U_EMRENDPATH_print(contents); break; - case U_EMR_CLOSEFIGURE: U_EMRCLOSEFIGURE_print(contents); break; - case U_EMR_FILLPATH: U_EMRFILLPATH_print(contents); break; - case U_EMR_STROKEANDFILLPATH: U_EMRSTROKEANDFILLPATH_print(contents); break; - case U_EMR_STROKEPATH: U_EMRSTROKEPATH_print(contents); break; - case U_EMR_FLATTENPATH: U_EMRFLATTENPATH_print(contents); break; - case U_EMR_WIDENPATH: U_EMRWIDENPATH_print(contents); break; - case U_EMR_SELECTCLIPPATH: U_EMRSELECTCLIPPATH_print(contents); break; - case U_EMR_ABORTPATH: U_EMRABORTPATH_print(contents); break; - case U_EMR_UNDEF69: U_EMRUNDEF69_print(contents); break; - case U_EMR_COMMENT: U_EMRCOMMENT_print(contents, blimit, off); break; - case U_EMR_FILLRGN: U_EMRFILLRGN_print(contents); break; - case U_EMR_FRAMERGN: U_EMRFRAMERGN_print(contents); break; - case U_EMR_INVERTRGN: U_EMRINVERTRGN_print(contents); break; - case U_EMR_PAINTRGN: U_EMRPAINTRGN_print(contents); break; - case U_EMR_EXTSELECTCLIPRGN: U_EMREXTSELECTCLIPRGN_print(contents); break; - case U_EMR_BITBLT: U_EMRBITBLT_print(contents); break; - case U_EMR_STRETCHBLT: U_EMRSTRETCHBLT_print(contents); break; - case U_EMR_MASKBLT: U_EMRMASKBLT_print(contents); break; - case U_EMR_PLGBLT: U_EMRPLGBLT_print(contents); break; - case U_EMR_SETDIBITSTODEVICE: U_EMRSETDIBITSTODEVICE_print(contents); break; - case U_EMR_STRETCHDIBITS: U_EMRSTRETCHDIBITS_print(contents); break; - case U_EMR_EXTCREATEFONTINDIRECTW: U_EMREXTCREATEFONTINDIRECTW_print(contents); break; - case U_EMR_EXTTEXTOUTA: U_EMREXTTEXTOUTA_print(contents); break; - case U_EMR_EXTTEXTOUTW: U_EMREXTTEXTOUTW_print(contents); break; - case U_EMR_POLYBEZIER16: U_EMRPOLYBEZIER16_print(contents); break; - case U_EMR_POLYGON16: U_EMRPOLYGON16_print(contents); break; - case U_EMR_POLYLINE16: U_EMRPOLYLINE16_print(contents); break; - case U_EMR_POLYBEZIERTO16: U_EMRPOLYBEZIERTO16_print(contents); break; - case U_EMR_POLYLINETO16: U_EMRPOLYLINETO16_print(contents); break; - case U_EMR_POLYPOLYLINE16: U_EMRPOLYPOLYLINE16_print(contents); break; - case U_EMR_POLYPOLYGON16: U_EMRPOLYPOLYGON16_print(contents); break; - case U_EMR_POLYDRAW16: U_EMRPOLYDRAW16_print(contents); break; - case U_EMR_CREATEMONOBRUSH: U_EMRCREATEMONOBRUSH_print(contents); break; - case U_EMR_CREATEDIBPATTERNBRUSHPT: U_EMRCREATEDIBPATTERNBRUSHPT_print(contents); break; - case U_EMR_EXTCREATEPEN: U_EMREXTCREATEPEN_print(contents); break; - case U_EMR_POLYTEXTOUTA: U_EMRPOLYTEXTOUTA_print(contents); break; - case U_EMR_POLYTEXTOUTW: U_EMRPOLYTEXTOUTW_print(contents); break; - case U_EMR_SETICMMODE: U_EMRSETICMMODE_print(contents); break; - case U_EMR_CREATECOLORSPACE: U_EMRCREATECOLORSPACE_print(contents); break; - case U_EMR_SETCOLORSPACE: U_EMRSETCOLORSPACE_print(contents); break; - case U_EMR_DELETECOLORSPACE: U_EMRDELETECOLORSPACE_print(contents); break; - case U_EMR_GLSRECORD: U_EMRGLSRECORD_print(contents); break; - case U_EMR_GLSBOUNDEDRECORD: U_EMRGLSBOUNDEDRECORD_print(contents); break; - case U_EMR_PIXELFORMAT: U_EMRPIXELFORMAT_print(contents); break; - case U_EMR_DRAWESCAPE: U_EMRDRAWESCAPE_print(contents); break; - case U_EMR_EXTESCAPE: U_EMREXTESCAPE_print(contents); break; - case U_EMR_UNDEF107: U_EMRUNDEF107_print(contents); break; - case U_EMR_SMALLTEXTOUT: U_EMRSMALLTEXTOUT_print(contents); break; - case U_EMR_FORCEUFIMAPPING: U_EMRFORCEUFIMAPPING_print(contents); break; - case U_EMR_NAMEDESCAPE: U_EMRNAMEDESCAPE_print(contents); break; - case U_EMR_COLORCORRECTPALETTE: U_EMRCOLORCORRECTPALETTE_print(contents); break; - case U_EMR_SETICMPROFILEA: U_EMRSETICMPROFILEA_print(contents); break; - case U_EMR_SETICMPROFILEW: U_EMRSETICMPROFILEW_print(contents); break; - case U_EMR_ALPHABLEND: U_EMRALPHABLEND_print(contents); break; - case U_EMR_SETLAYOUT: U_EMRSETLAYOUT_print(contents); break; - case U_EMR_TRANSPARENTBLT: U_EMRTRANSPARENTBLT_print(contents); break; - case U_EMR_UNDEF117: U_EMRUNDEF117_print(contents); break; - case U_EMR_GRADIENTFILL: U_EMRGRADIENTFILL_print(contents); break; - case U_EMR_SETLINKEDUFIS: U_EMRSETLINKEDUFIS_print(contents); break; - case U_EMR_SETTEXTJUSTIFICATION: U_EMRSETTEXTJUSTIFICATION_print(contents); break; - case U_EMR_COLORMATCHTOTARGETW: U_EMRCOLORMATCHTOTARGETW_print(contents); break; - case U_EMR_CREATECOLORSPACEW: U_EMRCREATECOLORSPACEW_print(contents); break; - default: U_EMRNOTIMPLEMENTED_print("?",contents); break; + case U_EMR_HEADER: U_EMRHEADER_print(record); break; + case U_EMR_POLYBEZIER: U_EMRPOLYBEZIER_print(record); break; + case U_EMR_POLYGON: U_EMRPOLYGON_print(record); break; + case U_EMR_POLYLINE: U_EMRPOLYLINE_print(record); break; + case U_EMR_POLYBEZIERTO: U_EMRPOLYBEZIERTO_print(record); break; + case U_EMR_POLYLINETO: U_EMRPOLYLINETO_print(record); break; + case U_EMR_POLYPOLYLINE: U_EMRPOLYPOLYLINE_print(record); break; + case U_EMR_POLYPOLYGON: U_EMRPOLYPOLYGON_print(record); break; + case U_EMR_SETWINDOWEXTEX: U_EMRSETWINDOWEXTEX_print(record); break; + case U_EMR_SETWINDOWORGEX: U_EMRSETWINDOWORGEX_print(record); break; + case U_EMR_SETVIEWPORTEXTEX: U_EMRSETVIEWPORTEXTEX_print(record); break; + case U_EMR_SETVIEWPORTORGEX: U_EMRSETVIEWPORTORGEX_print(record); break; + case U_EMR_SETBRUSHORGEX: U_EMRSETBRUSHORGEX_print(record); break; + case U_EMR_EOF: U_EMREOF_print(record); nSize=0; break; + case U_EMR_SETPIXELV: U_EMRSETPIXELV_print(record); break; + case U_EMR_SETMAPPERFLAGS: U_EMRSETMAPPERFLAGS_print(record); break; + case U_EMR_SETMAPMODE: U_EMRSETMAPMODE_print(record); break; + case U_EMR_SETBKMODE: U_EMRSETBKMODE_print(record); break; + case U_EMR_SETPOLYFILLMODE: U_EMRSETPOLYFILLMODE_print(record); break; + case U_EMR_SETROP2: U_EMRSETROP2_print(record); break; + case U_EMR_SETSTRETCHBLTMODE: U_EMRSETSTRETCHBLTMODE_print(record); break; + case U_EMR_SETTEXTALIGN: U_EMRSETTEXTALIGN_print(record); break; + case U_EMR_SETCOLORADJUSTMENT: U_EMRSETCOLORADJUSTMENT_print(record); break; + case U_EMR_SETTEXTCOLOR: U_EMRSETTEXTCOLOR_print(record); break; + case U_EMR_SETBKCOLOR: U_EMRSETBKCOLOR_print(record); break; + case U_EMR_OFFSETCLIPRGN: U_EMROFFSETCLIPRGN_print(record); break; + case U_EMR_MOVETOEX: U_EMRMOVETOEX_print(record); break; + case U_EMR_SETMETARGN: U_EMRSETMETARGN_print(record); break; + case U_EMR_EXCLUDECLIPRECT: U_EMREXCLUDECLIPRECT_print(record); break; + case U_EMR_INTERSECTCLIPRECT: U_EMRINTERSECTCLIPRECT_print(record); break; + case U_EMR_SCALEVIEWPORTEXTEX: U_EMRSCALEVIEWPORTEXTEX_print(record); break; + case U_EMR_SCALEWINDOWEXTEX: U_EMRSCALEWINDOWEXTEX_print(record); break; + case U_EMR_SAVEDC: U_EMRSAVEDC_print(record); break; + case U_EMR_RESTOREDC: U_EMRRESTOREDC_print(record); break; + case U_EMR_SETWORLDTRANSFORM: U_EMRSETWORLDTRANSFORM_print(record); break; + case U_EMR_MODIFYWORLDTRANSFORM: U_EMRMODIFYWORLDTRANSFORM_print(record); break; + case U_EMR_SELECTOBJECT: U_EMRSELECTOBJECT_print(record); break; + case U_EMR_CREATEPEN: U_EMRCREATEPEN_print(record); break; + case U_EMR_CREATEBRUSHINDIRECT: U_EMRCREATEBRUSHINDIRECT_print(record); break; + case U_EMR_DELETEOBJECT: U_EMRDELETEOBJECT_print(record); break; + case U_EMR_ANGLEARC: U_EMRANGLEARC_print(record); break; + case U_EMR_ELLIPSE: U_EMRELLIPSE_print(record); break; + case U_EMR_RECTANGLE: U_EMRRECTANGLE_print(record); break; + case U_EMR_ROUNDRECT: U_EMRROUNDRECT_print(record); break; + case U_EMR_ARC: U_EMRARC_print(record); break; + case U_EMR_CHORD: U_EMRCHORD_print(record); break; + case U_EMR_PIE: U_EMRPIE_print(record); break; + case U_EMR_SELECTPALETTE: U_EMRSELECTPALETTE_print(record); break; + case U_EMR_CREATEPALETTE: U_EMRCREATEPALETTE_print(record); break; + case U_EMR_SETPALETTEENTRIES: U_EMRSETPALETTEENTRIES_print(record); break; + case U_EMR_RESIZEPALETTE: U_EMRRESIZEPALETTE_print(record); break; + case U_EMR_REALIZEPALETTE: U_EMRREALIZEPALETTE_print(record); break; + case U_EMR_EXTFLOODFILL: U_EMREXTFLOODFILL_print(record); break; + case U_EMR_LINETO: U_EMRLINETO_print(record); break; + case U_EMR_ARCTO: U_EMRARCTO_print(record); break; + case U_EMR_POLYDRAW: U_EMRPOLYDRAW_print(record); break; + case U_EMR_SETARCDIRECTION: U_EMRSETARCDIRECTION_print(record); break; + case U_EMR_SETMITERLIMIT: U_EMRSETMITERLIMIT_print(record); break; + case U_EMR_BEGINPATH: U_EMRBEGINPATH_print(record); break; + case U_EMR_ENDPATH: U_EMRENDPATH_print(record); break; + case U_EMR_CLOSEFIGURE: U_EMRCLOSEFIGURE_print(record); break; + case U_EMR_FILLPATH: U_EMRFILLPATH_print(record); break; + case U_EMR_STROKEANDFILLPATH: U_EMRSTROKEANDFILLPATH_print(record); break; + case U_EMR_STROKEPATH: U_EMRSTROKEPATH_print(record); break; + case U_EMR_FLATTENPATH: U_EMRFLATTENPATH_print(record); break; + case U_EMR_WIDENPATH: U_EMRWIDENPATH_print(record); break; + case U_EMR_SELECTCLIPPATH: U_EMRSELECTCLIPPATH_print(record); break; + case U_EMR_ABORTPATH: U_EMRABORTPATH_print(record); break; + case U_EMR_UNDEF69: U_EMRUNDEF69_print(record); break; + case U_EMR_COMMENT: U_EMRCOMMENT_print(record, off); break; + case U_EMR_FILLRGN: U_EMRFILLRGN_print(record); break; + case U_EMR_FRAMERGN: U_EMRFRAMERGN_print(record); break; + case U_EMR_INVERTRGN: U_EMRINVERTRGN_print(record); break; + case U_EMR_PAINTRGN: U_EMRPAINTRGN_print(record); break; + case U_EMR_EXTSELECTCLIPRGN: U_EMREXTSELECTCLIPRGN_print(record); break; + case U_EMR_BITBLT: U_EMRBITBLT_print(record); break; + case U_EMR_STRETCHBLT: U_EMRSTRETCHBLT_print(record); break; + case U_EMR_MASKBLT: U_EMRMASKBLT_print(record); break; + case U_EMR_PLGBLT: U_EMRPLGBLT_print(record); break; + case U_EMR_SETDIBITSTODEVICE: U_EMRSETDIBITSTODEVICE_print(record); break; + case U_EMR_STRETCHDIBITS: U_EMRSTRETCHDIBITS_print(record); break; + case U_EMR_EXTCREATEFONTINDIRECTW: U_EMREXTCREATEFONTINDIRECTW_print(record); break; + case U_EMR_EXTTEXTOUTA: U_EMREXTTEXTOUTA_print(record); break; + case U_EMR_EXTTEXTOUTW: U_EMREXTTEXTOUTW_print(record); break; + case U_EMR_POLYBEZIER16: U_EMRPOLYBEZIER16_print(record); break; + case U_EMR_POLYGON16: U_EMRPOLYGON16_print(record); break; + case U_EMR_POLYLINE16: U_EMRPOLYLINE16_print(record); break; + case U_EMR_POLYBEZIERTO16: U_EMRPOLYBEZIERTO16_print(record); break; + case U_EMR_POLYLINETO16: U_EMRPOLYLINETO16_print(record); break; + case U_EMR_POLYPOLYLINE16: U_EMRPOLYPOLYLINE16_print(record); break; + case U_EMR_POLYPOLYGON16: U_EMRPOLYPOLYGON16_print(record); break; + case U_EMR_POLYDRAW16: U_EMRPOLYDRAW16_print(record); break; + case U_EMR_CREATEMONOBRUSH: U_EMRCREATEMONOBRUSH_print(record); break; + case U_EMR_CREATEDIBPATTERNBRUSHPT: U_EMRCREATEDIBPATTERNBRUSHPT_print(record); break; + case U_EMR_EXTCREATEPEN: U_EMREXTCREATEPEN_print(record); break; + case U_EMR_POLYTEXTOUTA: U_EMRPOLYTEXTOUTA_print(record); break; + case U_EMR_POLYTEXTOUTW: U_EMRPOLYTEXTOUTW_print(record); break; + case U_EMR_SETICMMODE: U_EMRSETICMMODE_print(record); break; + case U_EMR_CREATECOLORSPACE: U_EMRCREATECOLORSPACE_print(record); break; + case U_EMR_SETCOLORSPACE: U_EMRSETCOLORSPACE_print(record); break; + case U_EMR_DELETECOLORSPACE: U_EMRDELETECOLORSPACE_print(record); break; + case U_EMR_GLSRECORD: U_EMRGLSRECORD_print(record); break; + case U_EMR_GLSBOUNDEDRECORD: U_EMRGLSBOUNDEDRECORD_print(record); break; + case U_EMR_PIXELFORMAT: U_EMRPIXELFORMAT_print(record); break; + case U_EMR_DRAWESCAPE: U_EMRDRAWESCAPE_print(record); break; + case U_EMR_EXTESCAPE: U_EMREXTESCAPE_print(record); break; + case U_EMR_UNDEF107: U_EMRUNDEF107_print(record); break; + case U_EMR_SMALLTEXTOUT: U_EMRSMALLTEXTOUT_print(record); break; + case U_EMR_FORCEUFIMAPPING: U_EMRFORCEUFIMAPPING_print(record); break; + case U_EMR_NAMEDESCAPE: U_EMRNAMEDESCAPE_print(record); break; + case U_EMR_COLORCORRECTPALETTE: U_EMRCOLORCORRECTPALETTE_print(record); break; + case U_EMR_SETICMPROFILEA: U_EMRSETICMPROFILEA_print(record); break; + case U_EMR_SETICMPROFILEW: U_EMRSETICMPROFILEW_print(record); break; + case U_EMR_ALPHABLEND: U_EMRALPHABLEND_print(record); break; + case U_EMR_SETLAYOUT: U_EMRSETLAYOUT_print(record); break; + case U_EMR_TRANSPARENTBLT: U_EMRTRANSPARENTBLT_print(record); break; + case U_EMR_UNDEF117: U_EMRUNDEF117_print(record); break; + case U_EMR_GRADIENTFILL: U_EMRGRADIENTFILL_print(record); break; + case U_EMR_SETLINKEDUFIS: U_EMRSETLINKEDUFIS_print(record); break; + case U_EMR_SETTEXTJUSTIFICATION: U_EMRSETTEXTJUSTIFICATION_print(record); break; + case U_EMR_COLORMATCHTOTARGETW: U_EMRCOLORMATCHTOTARGETW_print(record); break; + case U_EMR_CREATECOLORSPACEW: U_EMRCREATECOLORSPACEW_print(record); break; + default: U_EMRNOTIMPLEMENTED_print("?",record); break; } //end of switch - return(size); + return(nSize); } diff --git a/src/libuemf/uemf_print.h b/src/libuemf/uemf_print.h index 896de659e..088a8a302 100644 --- a/src/libuemf/uemf_print.h +++ b/src/libuemf/uemf_print.h @@ -6,11 +6,11 @@ /* File: uemf_print.h -Version: 0.0.5 -Date: 14-FEB-2013 +Version: 0.0.9 +Date: 21-MAY-2015 Author: David Mathog, Biology Division, Caltech email: mathog@caltech.edu -Copyright: 2013 David Mathog and California Institute of Technology (Caltech) +Copyright: 2015 David Mathog and California Institute of Technology (Caltech) */ #ifndef _UEMF_PRINT_ @@ -21,6 +21,9 @@ extern "C" { #endif //! \cond +/* prototypes for miscellaneous */ +uint32_t lu_crc32(const char *record, uint32_t Size); + /* prototypes for objects used in EMR records */ void hexbytes_print(uint8_t *buf,unsigned int num); void colorref_print(U_COLORREF color); @@ -44,17 +47,17 @@ void panose_print(U_PANOSE panose); void logfont_print(U_LOGFONT lf); void logfont_panose_print(U_LOGFONT_PANOSE lfp); void bitmapinfoheader_print(const char *Bmih); -void bitmapinfo_print(const char *Bmi); +void bitmapinfo_print(const char *Bmi, const char *blimit); void blend_print(U_BLEND blend); void extlogpen_print(const PU_EXTLOGPEN elp); void logpen_print(U_LOGPEN lp); void logpltntry_print(U_LOGPLTNTRY lpny); void logpalette_print(const PU_LOGPALETTE lp); void rgndataheader_print(U_RGNDATAHEADER rdh); -void rgndata_print(const PU_RGNDATA rd); +void rgndata_print(const PU_RGNDATA rd, const char *blimit); void coloradjustment_print(U_COLORADJUSTMENT ca); void pixelformatdescriptor_print(U_PIXELFORMATDESCRIPTOR pfd); -void emrtext_print(const char *emt, const char *record, int type); +void emrtext_print(const char *emt, const char *record, const char *blimit, int type); /* prototypes for EMR records */ void U_EMRNOTIMPLEMENTED_print(const char *name, const char *contents, int recnum, int off); @@ -126,7 +129,7 @@ void U_EMRFLATTENPATH_print(const char *contents); void U_EMRWIDENPATH_print(const char *contents); void U_EMRSELECTCLIPPATH_print(const char *contents); void U_EMRABORTPATH_print(const char *contents); -void U_EMRCOMMENT_print(const char *contents, const char *blimit, size_t off); +void U_EMRCOMMENT_print(const char *contents, size_t off); void U_EMRFILLRGN_print(const char *contents); void U_EMRFRAMERGN_print(const char *contents); void U_EMRINVERTRGN_print(const char *contents); diff --git a/src/libuemf/uemf_safe.c b/src/libuemf/uemf_safe.c new file mode 100644 index 000000000..36284d60e --- /dev/null +++ b/src/libuemf/uemf_safe.c @@ -0,0 +1,1202 @@ +/** + @file uemf_safe.c + + @brief Functions for checking EMF records for memory issues. + + EMF records come in a variety of sizes, and some types have variable sizes. + These functions check the record types and report if there are any issues + that could cause a memory access problem. All counts and offsets are examined + and the data structure checked so that no referenced byte is outside of the + declared size of the record. + + Many variables are initialized to zero even though they will always be set because + some versions of gcc give spurious "may be used uninitialized" warnings otherwise. +*/ + +/* +File: uemf_safe.c +Version: 0.0.4 +Date: 23-APR-2015 +Author: David Mathog, Biology Division, Caltech +email: mathog@caltech.edu +Copyright: 2015 David Mathog and California Institute of Technology (Caltech) +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <stddef.h> /* for offsetof() macro */ +#include "uemf.h" +#include "uemf_endian.h" // for u_emf_record_sizeok + +// hide almost everuything in here from Doxygen +//! \cond + +/** + \brief Test a U_EXTLOGPEN object. + \param elp PU_EXTLOGPEN object + \param blimit one byte past the end of the record +*/ +int extlogpen_safe( + PU_EXTLOGPEN elp, + const char *blimit + ){ + int count=elp->elpNumEntries; + if(IS_MEM_UNSAFE(&(elp->elpStyleEntry), count*4, blimit))return(0); + return(1); +} + +/** + \brief Test a U_EMRTEXT record + \param pemt Pointer to a U_EMRTEXT record + \param record Pointer to the start of the record which contains this U_EMRTEXT + \param blimit one byte past the end of the record. +*/ +int emrtext_safe( + PU_EMRTEXT pemt, + const char *record, + const char *blimit + ){ + int off; + uint32_t count = pemt->nChars; + uint32_t fOptions = pemt->fOptions; + uint32_t offDx = 0; + off = sizeof(U_EMRTEXT); + if(!(fOptions & U_ETO_NO_RECT)){ + if(IS_MEM_UNSAFE(pemt, sizeof(U_RECTL), blimit))return(0); + off+=sizeof(U_RECTL); + } + offDx = *(uint32_t *)((char *)pemt +off); + if(IS_MEM_UNSAFE(pemt, off + 4, blimit))return(0); + if(IS_MEM_UNSAFE(record, offDx + count*4, blimit))return(0); + return(1); +} + +/** + \return 1 on success, 0 on failure + \brief Test a U_RGNDATA object. + \param rd pointer to a U_RGNDATA object. + \param cbRgnData size of the U_RGNDATA object. +*/ +int rgndata_safe( + PU_RGNDATA rd, + int cbRgnData + ){ + int count = rd->rdh.nCount; + if(4*count + (int)sizeof(U_RGNDATAHEADER) > cbRgnData)return(0); + return(1); +} + + +/** + \return 1 on success, 0 on failure + \brief Test a U_BITMAPINFO object. + \param Bmi pointer to a U_BITMAPINFO object. + \param blimit one byte past the end of the record. +*/ +int bitmapinfo_safe( + const char *Bmi, + const char *blimit + ){ + int ClrUsed; + if(IS_MEM_UNSAFE(Bmi, offsetof(U_BITMAPINFO,bmiHeader) + sizeof(U_BITMAPINFOHEADER), blimit))return(0); + ClrUsed = get_real_color_count(Bmi + offsetof(U_BITMAPINFO,bmiHeader)); + if(ClrUsed && IS_MEM_UNSAFE(Bmi, offsetof(U_BITMAPINFO,bmiColors) + ClrUsed*sizeof(U_RGBQUAD), blimit))return(0); + return(1); +} + +/** + \brief Check that the bitmap in the specified DIB is compatible with the record size + + \return 1 on success, 0 on failure + \param record EMF record that contains a DIB pixel array + \param iUsage DIBcolors Enumeration + \param offBmi offset from the start of the record to the start of the bitmapinfo structure + \param cbBmi declared space for the bitmapinfo structure in the record + \param offBits offset from the start of the record to the start of the bitmap + \param cbBits declared space for the bitmap in the record (amount used may be less than this) + \param blimit one byte past the end of the record. + + This method can only test DIBs that hold Microsoft's various bitmap types. PNG or JPG is just a bag + of bytes, and there is no possible way to derive from the known width and height how big it should be. +*/ +int DIB_safe( + const char *record, + uint32_t iUsage, + uint32_t offBmi, + uint32_t cbBmi, + uint32_t offBits, + uint32_t cbBits, + const char *blimit + ){ + int dibparams = U_BI_UNKNOWN; // type of image not yet determined + const char *px = NULL; // DIB pixels + const U_RGBQUAD *ct = NULL; // DIB color table + int bs; + int usedbytes; + + if(!cbBmi)return(1); // No DIB in a record where it is optional + if(IS_MEM_UNSAFE(record, offBmi + cbBmi, blimit))return(0); + if(!bitmapinfo_safe(record + offBmi, blimit))return(0); // checks the number of colors + if(cbBits && IS_MEM_UNSAFE(record, offBits + cbBits, blimit))return(0); + if(iUsage == U_DIB_RGB_COLORS){ + uint32_t width, height, colortype, numCt, invert; // these values will be set in get_DIB_params + // next call returns pointers and values, but allocates no memory + dibparams = get_DIB_params(record, offBits, offBmi, &px, (const U_RGBQUAD **) &ct, + &numCt, &width, &height, &colortype, &invert); + + // sanity checking + if(numCt && colortype >= U_BCBM_COLOR16)return(0); //color tables not used above 16 bit pixels + if(!numCt && colortype < U_BCBM_COLOR16)return(0); //color tables mandatory for < 16 bit + + if(dibparams ==U_BI_RGB){ + // this is the only DIB type where we can calculate how big it should be when stored in the EMF file + bs = colortype/8; + if(bs<1){ + usedbytes = (width*colortype + 7)/8; // width of line in fully and partially occupied bytes + } + else { + usedbytes = width*bs; + } + if(IS_MEM_UNSAFE(record+offBits, usedbytes, blimit))return(0); + } + } + return(1); +} + + +/* ********************************************************************************************** +These functions contain shared code used by various U_EMR*_safe functions. These should NEVER be called +by end user code and to further that end prototypes are NOT provided and they are hidden from Doxygen. + + + These all have this form: + + void core1_safe(const char *record){ + + but some do not actually use torev. + + + +*********************************************************************************************** */ + +// all core*_safe call this, U_EMRSETMARGN_safe and some others all it directly +// numbered as core5 to be consistent with uemf.c, but must appear before the others as there is no prototype +// sizeof(U_ENHMETARECORD) bytes in the record +int core5_safe(const char *record, int minSize){ + PU_EMR pEmr = (PU_EMR)(record); + if((int) pEmr->nSize < minSize)return(0); + return(1); +} + +// Functions with the same form starting with U_EMRPOLYBEZIER_safe +int core1_safe(const char *record){ + if(!core5_safe(record, U_SIZE_EMRPOLYLINETO))return(0); + PU_EMRPOLYLINETO pEmr = (PU_EMRPOLYLINETO) (record); + int count=pEmr->cptl; + const char *blimit = record + pEmr->emr.nSize; + if(IS_MEM_UNSAFE(pEmr->aptl, count*sizeof(U_POINTL), blimit))return(0); + return(1); +} + +// Functions with the same form starting with U_EMRPOLYPOLYLINE_safe +int core2_safe(const char *record){ + if(!core5_safe(record, U_SIZE_EMRPOLYPOLYLINE))return(0); + PU_EMRPOLYPOLYLINE pEmr = (PU_EMRPOLYPOLYLINE) (record); + int count = pEmr->cptl; + int nPolys = pEmr->nPolys; + const char * blimit = record + pEmr->emr.nSize; + if(IS_MEM_UNSAFE(pEmr->aPolyCounts, nPolys*4, blimit))return(0); + record += sizeof(U_EMRPOLYPOLYLINE) - 4 + sizeof(uint32_t)* nPolys; + if(IS_MEM_UNSAFE(record, count*sizeof(U_POINTL), blimit))return(0); + return(1); +} + + +// Functions with the same form starting with U_EMRSETMAPMODE_safe +int core3_safe(const char *record){ + if(!core5_safe(record, U_SIZE_EMRSETMAPMODE))return(0); + return(1); +} + +// Functions taking a single U_RECT or U_RECTL, starting with U_EMRELLIPSE_safe, also U_EMRFILLPATH_safe, +int core4_safe(const char *record){ + if(!core5_safe(record, U_SIZE_EMRELLIPSE))return(0); + return(1); +} + +// Functions with the same form starting with U_EMRPOLYBEZIER16_safe +int core6_safe(const char *record){ + if(!core5_safe(record, U_SIZE_EMRPOLYBEZIER16))return(0); + PU_EMRPOLYBEZIER16 pEmr = (PU_EMRPOLYBEZIER16) (record); + int count=pEmr->cpts; + const char *blimit = record + pEmr->emr.nSize; + if(IS_MEM_UNSAFE(pEmr->apts, count*sizeof(U_POINT16), blimit))return(0); + return(1); +} + + +// Records with the same form starting with U_EMRSETWINDOWEXTEX_safe, that is, all with two uint32_t values after the emr +int core7_safe(const char *record){ + if(!core5_safe(record, U_SIZE_EMRSETWINDOWEXTEX))return(0); + return(1); +} + +// For U_EMREXTTEXTOUTA and U_EMREXTTEXTOUTW, type=0 for the first one +int core8_safe(const char *record){ + if(!core5_safe(record, U_SIZE_EMREXTTEXTOUTA))return(0); + PU_EMREXTTEXTOUTA pEmr = (PU_EMREXTTEXTOUTA) (record); + const char *blimit = record + pEmr->emr.nSize; + if(!emrtext_safe(&(pEmr->emrtext),record,blimit))return(0); + return(1); +} + +// Functions that take a rect and a pair of points, starting with U_EMRARC_safe +int core9_safe(const char *record){ + if(!core5_safe(record, U_SIZE_EMRARC))return(0); + return(1); +} + +// Functions with the same form starting with U_EMRPOLYPOLYLINE16_safe +int core10_safe(const char *record){ + if(!core5_safe(record, U_SIZE_EMRPOLYPOLYLINE16))return(0); + PU_EMRPOLYPOLYLINE16 pEmr = (PU_EMRPOLYPOLYLINE16) (record); + int count = pEmr->cpts; + int nPolys = pEmr->nPolys; + const char *blimit = record + pEmr->emr.nSize; + if(IS_MEM_UNSAFE(pEmr->aPolyCounts, nPolys*4, blimit))return(0); + record += sizeof(U_EMRPOLYPOLYLINE16) - 4 + sizeof(uint32_t)* nPolys; + if(IS_MEM_UNSAFE(record, count*sizeof(U_POINT16), blimit))return(0); + return(1); +} + +// Functions with the same form starting with U_EMRINVERTRGN_safe and U_EMRPAINTRGN_safe, +int core11_safe(const char *record){ + if(!core5_safe(record, U_SIZE_EMRINVERTRGN))return(0); + PU_EMRINVERTRGN pEmr = (PU_EMRINVERTRGN)(record); + int cbRgnData = pEmr->cbRgnData; + const char *blimit = record + pEmr->emr.nSize; + if(IS_MEM_UNSAFE(pEmr->RgnData, cbRgnData, blimit))return(0); + return(rgndata_safe(pEmr->RgnData, cbRgnData)); +} + + +// common code for U_EMRCREATEMONOBRUSH_safe and U_EMRCREATEDIBPATTERNBRUSHPT_safe, +int core12_safe(const char *record){ + if(!core5_safe(record, U_SIZE_EMRCREATEMONOBRUSH))return(0); + PU_EMRCREATEMONOBRUSH pEmr = (PU_EMRCREATEMONOBRUSH) (record); + const char *blimit = record + pEmr->emr.nSize; + U_OFFBMI offBmi = pEmr->offBmi; + U_CBBMI cbBmi = pEmr->cbBmi; + U_OFFBITS offBits = pEmr->offBits; + U_CBBITS cbBits = pEmr->cbBits; + uint32_t iUsage = pEmr->iUsage; + return(DIB_safe(record, iUsage, offBmi, cbBmi, offBits, cbBits, blimit)); +} + +// common code for U_EMRALPHABLEND_safe and U_EMRTRANSPARENTBLT_safe, +int core13_safe(const char *record){ + if(!core5_safe(record, U_SIZE_EMRALPHABLEND))return(0); + PU_EMRALPHABLEND pEmr = (PU_EMRALPHABLEND) (record); + const char *blimit = record + pEmr->emr.nSize; + U_OFFBMISRC offBmiSrc = pEmr->offBmiSrc; + U_CBBMISRC cbBmiSrc = pEmr->cbBmiSrc; + U_OFFBITSSRC offBitsSrc = pEmr->offBitsSrc; + U_CBBITS cbBitsSrc = pEmr->cbBitsSrc; + uint32_t iUsageSrc = pEmr->iUsageSrc; + return(DIB_safe(record, iUsageSrc, offBmiSrc, cbBmiSrc, offBitsSrc, cbBitsSrc, blimit)); +} + +/* ********************************************************************************************** +These are the core EMR_safe functions, each converts a particular type of record. +All operate in place on the chunk of memory holding that record. +Some of these have offsets or counts which, if corrupt or evil would result in access outside + the record. These cases return a status value of 0 if that happens, 1 on success. Other + records which do not have these issues do not return a status value. +They are listed in order by the corresponding U_EMR_* index number. +*********************************************************************************************** */ + +/** + All of the record level (hidden) functions have this form: + \brief Convert a pointer to a U_EMR_whatever record which has not been implemented. + \param record pointer to a buffer holding the EMR record + \param torev 1 for native to reversed, 0 for reversed to native +*/ +int U_EMRNOTIMPLEMENTED_safe(const char *record){ + fprintf(stderr,"EMF WARNING: could not safety check record because that type has not been implemented!\n"); + return(core5_safe(record, sizeof(U_EMR))); +} + +// U_EMRHEADER 1 +int U_EMRHEADER_safe(const char *record){ + // use _MIN form so that it accepts very old EMF files + return(core5_safe(record, U_SIZE_EMRHEADER_MIN)); +} + +// U_EMRPOLYBEZIER 2 +int U_EMRPOLYBEZIER_safe(const char *record){ + return(core1_safe(record)); +} + +// U_EMRPOLYGON 3 +int U_EMRPOLYGON_safe(const char *record){ + return(core1_safe(record)); +} + +// U_EMRPOLYLINE 4 +int U_EMRPOLYLINE_safe(const char *record){ + return(core1_safe(record)); +} + +// U_EMRPOLYBEZIERTO 5 +int U_EMRPOLYBEZIERTO_safe(const char *record){ + return(core1_safe(record)); +} + +// U_EMRPOLYLINETO 6 +int U_EMRPOLYLINETO_safe(const char *record){ + return(core1_safe(record)); +} + +// U_EMRPOLYPOLYLINE 7 +int U_EMRPOLYPOLYLINE_safe(const char *record){ + return(core2_safe(record)); +} + +// U_EMRPOLYPOLYGON 8 +int U_EMRPOLYPOLYGON_safe(const char *record){ + return(core2_safe(record)); +} + +// U_EMRSETWINDOWEXTEX 9 +int U_EMRSETWINDOWEXTEX_safe(const char *record){ + return(core7_safe(record)); +} + +// U_EMRSETWINDOWORGEX 10 +int U_EMRSETWINDOWORGEX_safe(const char *record){ + return(core7_safe(record)); +} + +// U_EMRSETVIEWPORTEXTEX 11 +int U_EMRSETVIEWPORTEXTEX_safe(const char *record){ + return(core7_safe(record)); +} + +// U_EMRSETVIEWPORTORGEX 12 +int U_EMRSETVIEWPORTORGEX_safe(const char *record){ + return(core7_safe(record)); +} + +// U_EMRSETBRUSHORGEX 13 +int U_EMRSETBRUSHORGEX_safe(const char *record){ + return(core7_safe(record)); +} + +// U_EMREOF 14 +int U_EMREOF_safe(const char *record){ + if(!core5_safe(record, U_SIZE_EMREOF))return(0); + PU_EMREOF pEmr = (PU_EMREOF)(record); + const char *blimit = record + pEmr->emr.nSize; + int cbPalEntries=pEmr->cbPalEntries; + if(cbPalEntries){ + if(IS_MEM_UNSAFE(record, pEmr->offPalEntries + 2*2, blimit))return(0);// 2 16 bit values in U_LOGPALLETE + } + int off = sizeof(U_EMREOF) + 4 * cbPalEntries; + if(IS_MEM_UNSAFE(record, off + 4, blimit))return(0); + return(1); +} + + +// U_EMRSETPIXELV 15 +int U_EMRSETPIXELV_safe(const char *record){ + return(core5_safe(record, U_SIZE_EMRSETPIXELV)); + +} + + +// U_EMRSETMAPPERFLAGS 16 +int U_EMRSETMAPPERFLAGS_safe(const char *record){ + return(core5_safe(record, U_SIZE_EMRSETMAPPERFLAGS)); +} + + +// U_EMRSETMAPMODE 17 +int U_EMRSETMAPMODE_safe(const char *record){ + return(core3_safe(record)); +} + +// U_EMRSETBKMODE 18 +int U_EMRSETBKMODE_safe(const char *record){ + return(core3_safe(record)); +} + +// U_EMRSETPOLYFILLMODE 19 +int U_EMRSETPOLYFILLMODE_safe(const char *record){ + return(core3_safe(record)); +} + +// U_EMRSETROP2 20 +int U_EMRSETROP2_safe(const char *record){ + return(core3_safe(record)); +} + +// U_EMRSETSTRETCHBLTMODE 21 +int U_EMRSETSTRETCHBLTMODE_safe(const char *record){ + return(core3_safe(record)); +} + +// U_EMRSETTEXTALIGN 22 +int U_EMRSETTEXTALIGN_safe(const char *record){ + return(core3_safe(record)); +} + +// U_EMRSETCOLORADJUSTMENT 23 +int U_EMRSETCOLORADJUSTMENT_safe(const char *record){ + return(core5_safe(record, U_SIZE_EMRSETCOLORADJUSTMENT)); +} + +// U_EMRSETTEXTCOLOR 24 +int U_EMRSETTEXTCOLOR_safe(const char *record){ + return(core5_safe(record, U_SIZE_EMRSETTEXTCOLOR)); +} + +// U_EMRSETBKCOLOR 25 +int U_EMRSETBKCOLOR_safe(const char *record){ + return(core5_safe(record, U_SIZE_EMRSETBKCOLOR)); +} + +// U_EMROFFSETCLIPRGN 26 +int U_EMROFFSETCLIPRGN_safe(const char *record){ + return(core7_safe(record)); +} + +// U_EMRMOVETOEX 27 +int U_EMRMOVETOEX_safe(const char *record){ + return(core7_safe(record)); +} + +// U_EMRSETMETARGN 28 +int U_EMRSETMETARGN_safe(const char *record){ + return(core5_safe(record, U_SIZE_EMRSETMETARGN)); +} + +// U_EMREXCLUDECLIPRECT 29 +int U_EMREXCLUDECLIPRECT_safe(const char *record){ + return(core4_safe(record)); +} + +// U_EMRINTERSECTCLIPRECT 30 +int U_EMRINTERSECTCLIPRECT_safe(const char *record){ + return(core4_safe(record)); +} + +// U_EMRSCALEVIEWPORTEXTEX 31 +int U_EMRSCALEVIEWPORTEXTEX_safe(const char *record){ + return(core4_safe(record)); +} + +// U_EMRSCALEWINDOWEXTEX 32 +int U_EMRSCALEWINDOWEXTEX_safe(const char *record){ + return(core4_safe(record)); +} + +// U_EMRSAVEDC 33 +int U_EMRSAVEDC_safe(const char *record){ + return(core5_safe(record, U_SIZE_EMRSAVEDC)); +} + +// U_EMRRESTOREDC 34 +int U_EMRRESTOREDC_safe(const char *record){ + return(core3_safe(record)); +} + +// U_EMRSETWORLDTRANSFORM 35 +int U_EMRSETWORLDTRANSFORM_safe(const char *record){ + return(core5_safe(record, U_SIZE_EMRSETWORLDTRANSFORM)); +} + +// U_EMRMODIFYWORLDTRANSFORM 36 +int U_EMRMODIFYWORLDTRANSFORM_safe(const char *record){ + return(core5_safe(record, U_SIZE_EMRMODIFYWORLDTRANSFORM)); +} + +// U_EMRSELECTOBJECT 37 +int U_EMRSELECTOBJECT_safe(const char *record){ + return(core5_safe(record, U_SIZE_EMRSELECTOBJECT)); +} + +// U_EMRCREATEPEN 38 +int U_EMRCREATEPEN_safe(const char *record){ + return(core5_safe(record, U_SIZE_EMRCREATEPEN)); +} + +// U_EMRCREATEBRUSHINDIRECT 39 +int U_EMRCREATEBRUSHINDIRECT_safe(const char *record){ + return(core5_safe(record, U_SIZE_EMRCREATEBRUSHINDIRECT)); +} + +// U_EMRDELETEOBJECT 40 +int U_EMRDELETEOBJECT_safe(const char *record){ + return(core5_safe(record, U_SIZE_EMRDELETEOBJECT)); + +} + +// U_EMRANGLEARC 41 +int U_EMRANGLEARC_safe(const char *record){ + return(core5_safe(record, U_SIZE_EMRANGLEARC)); +} + +// U_EMRELLIPSE 42 +int U_EMRELLIPSE_safe(const char *record){ + return(core4_safe(record)); +} + +// U_EMRRECTANGLE 43 +int U_EMRRECTANGLE_safe(const char *record){ + return(core4_safe(record)); +} + +// U_EMRROUNDRECT 44 +int U_EMRROUNDRECT_safe(const char *record){ + return(core5_safe(record, U_SIZE_EMRROUNDRECT)); +} + +// U_EMRARC 45 +int U_EMRARC_safe(const char *record){ + return(core9_safe(record)); +} + +// U_EMRCHORD 46 +int U_EMRCHORD_safe(const char *record){ + return(core9_safe(record)); +} + +// U_EMRPIE 47 +int U_EMRPIE_safe(const char *record){ + return(core9_safe(record)); +} + +// U_EMRSELECTPALETTE 48 +int U_EMRSELECTPALETTE_safe(const char *record){ + return(core3_safe(record)); +} + +// U_EMRCREATEPALETTE 49 +int U_EMRCREATEPALETTE_safe(const char *record){ + return(core5_safe(record, U_SIZE_EMRCREATEPALETTE)); +} + +// U_EMRSETPALETTEENTRIES 50 +int U_EMRSETPALETTEENTRIES_safe(const char *record){ + return(core5_safe(record, U_SIZE_EMRSETPALETTEENTRIES)); +} + +// U_EMRRESIZEPALETTE 51 +int U_EMRRESIZEPALETTE_safe(const char *record){ + return(core7_safe(record)); +} + +// U_EMRREALIZEPALETTE 52 +int U_EMRREALIZEPALETTE_safe(const char *record){ + return(core5_safe(record, U_SIZE_EMRREALIZEPALETTE)); +} + +// U_EMREXTFLOODFILL 53 +int U_EMREXTFLOODFILL_safe(const char *record){ + return(core5_safe(record, U_SIZE_EMREXTFLOODFILL)); +} + +// U_EMRLINETO 54 +int U_EMRLINETO_safe(const char *record){ + return(core7_safe(record)); +} + +// U_EMRARCTO 55 +int U_EMRARCTO_safe(const char *record){ + return(core9_safe(record)); +} + +// U_EMRPOLYDRAW 56 +int U_EMRPOLYDRAW_safe(const char *record){ + if(!core5_safe(record, U_SIZE_EMRPOLYDRAW))return(0); + PU_EMRPOLYDRAW pEmr = (PU_EMRPOLYDRAW)(record); + int count = pEmr->cptl; + const char *blimit = record + pEmr->emr.nSize; + if(IS_MEM_UNSAFE(pEmr->aptl, count*sizeof(U_POINTL), blimit))return(0); + return(1); +} + +// U_EMRSETARCDIRECTION 57 +int U_EMRSETARCDIRECTION_safe(const char *record){ + return(core3_safe(record)); +} + +// U_EMRSETMITERLIMIT 58 +int U_EMRSETMITERLIMIT_safe(const char *record){ + return(core3_safe(record)); +} + + +// U_EMRBEGINPATH 59 +int U_EMRBEGINPATH_safe(const char *record){ + return(core5_safe(record, U_SIZE_EMRBEGINPATH)); +} + +// U_EMRENDPATH 60 +int U_EMRENDPATH_safe(const char *record){ + return(core5_safe(record, U_SIZE_EMRENDPATH)); +} + +// U_EMRCLOSEFIGURE 61 +int U_EMRCLOSEFIGURE_safe(const char *record){ + return(core5_safe(record, U_SIZE_EMRCLOSEFIGURE)); +} + +// U_EMRFILLPATH 62 +int U_EMRFILLPATH_safe(const char *record){ + return(core4_safe(record)); +} + +// U_EMRSTROKEANDFILLPATH 63 +int U_EMRSTROKEANDFILLPATH_safe(const char *record){ + return(core4_safe(record)); +} + +// U_EMRSTROKEPATH 64 +int U_EMRSTROKEPATH_safe(const char *record){ + return(core4_safe(record)); +} + +// U_EMRFLATTENPATH 65 +int U_EMRFLATTENPATH_safe(const char *record){ + return(core5_safe(record, U_SIZE_EMRFLATTENPATH)); +} + +// U_EMRWIDENPATH 66 +int U_EMRWIDENPATH_safe(const char *record){ + return(core5_safe(record, U_SIZE_EMRWIDENPATH)); +} + +// U_EMRSELECTCLIPPATH 67 +int U_EMRSELECTCLIPPATH_safe(const char *record){ + return(core3_safe(record)); +} + +// U_EMRABORTPATH 68 +int U_EMRABORTPATH_safe(const char *record){ + return(core5_safe(record, U_SIZE_EMRABORTPATH)); +} + +// U_EMRUNDEF69 69 +#define U_EMRUNDEF69_safe(A) U_EMRNOTIMPLEMENTED_safe(A) //!< Not implemented. + +// U_EMRCOMMENT 70 Comment (any binary data, interpretation is program specific) +int U_EMRCOMMENT_safe(const char *record){ + if(!core5_safe(record, U_SIZE_EMRCOMMENT))return(0); + PU_EMRCOMMENT pEmr = (PU_EMRCOMMENT)(record); + int cbData = pEmr->cbData; + const char *blimit =record + pEmr->emr.nSize; + if(IS_MEM_UNSAFE(record, cbData + sizeof(U_SIZE_EMRCOMMENT), blimit))return(0); + return(1); +} + +// U_EMRFILLRGN 71 +int U_EMRFILLRGN_safe(const char *record){ + if(!core5_safe(record, U_SIZE_EMRFILLRGN))return(0); + PU_EMRFILLRGN pEmr = (PU_EMRFILLRGN)(record); + int cbRgnData = pEmr->cbRgnData; + const char *blimit = record + pEmr->emr.nSize; + if(IS_MEM_UNSAFE(pEmr->RgnData, cbRgnData, blimit))return(0); + return(rgndata_safe(pEmr->RgnData, cbRgnData)); +} + +// U_EMRFRAMERGN 72 +int U_EMRFRAMERGN_safe(const char *record){ + if(!core5_safe(record, U_SIZE_EMRFRAMERGN))return(0); + PU_EMRFRAMERGN pEmr = (PU_EMRFRAMERGN)(record); + int cbRgnData = pEmr->cbRgnData; + const char *blimit = record + pEmr->emr.nSize; + if(IS_MEM_UNSAFE(pEmr->RgnData, cbRgnData, blimit))return(0); + return(rgndata_safe(pEmr->RgnData, cbRgnData)); +} + +// U_EMRINVERTRGN 73 +int U_EMRINVERTRGN_safe(const char *record){ + return(core11_safe(record)); +} + +// U_EMRPAINTRGN 74 +int U_EMRPAINTRGN_safe(const char *record){ + return(core11_safe(record)); +} + +// U_EMREXTSELECTCLIPRGN 75 +int U_EMREXTSELECTCLIPRGN_safe(const char *record){ + if(!core5_safe(record, U_SIZE_EMREXTSELECTCLIPRGN))return(0); + PU_EMREXTSELECTCLIPRGN pEmr = (PU_EMREXTSELECTCLIPRGN)(record); + int cbRgnData = pEmr->cbRgnData; + const char *blimit = record + pEmr->emr.nSize; + if(IS_MEM_UNSAFE(pEmr->RgnData, cbRgnData, blimit))return(0); + return(rgndata_safe(pEmr->RgnData, cbRgnData)); +} + +// U_EMRBITBLT 76 +int U_EMRBITBLT_safe(const char *record){ + if(!core5_safe(record, U_SIZE_EMRBITBLT))return(0); + PU_EMRBITBLT pEmr = (PU_EMRBITBLT) (record); + const char *blimit = record + pEmr->emr.nSize; + U_OFFBMISRC offBmiSrc = pEmr->offBmiSrc; + U_CBBMISRC cbBmiSrc = pEmr->cbBmiSrc; + U_OFFBITSSRC offBitsSrc = pEmr->offBitsSrc; + U_CBBITS cbBitsSrc = pEmr->cbBitsSrc; + uint32_t iUsageSrc = pEmr->iUsageSrc; + return(DIB_safe(record, iUsageSrc, offBmiSrc, cbBmiSrc, offBitsSrc, cbBitsSrc, blimit)); +} + +// U_EMRSTRETCHBLT 77 +int U_EMRSTRETCHBLT_safe(const char *record){ + if(!core5_safe(record, U_SIZE_EMRSTRETCHBLT))return(0); + PU_EMRBITBLT pEmr = (PU_EMRBITBLT) (record); + const char *blimit = record + pEmr->emr.nSize; + U_OFFBMISRC offBmiSrc = pEmr->offBmiSrc; + U_CBBMISRC cbBmiSrc = pEmr->cbBmiSrc; + U_OFFBITSSRC offBitsSrc = pEmr->offBitsSrc; + U_CBBITS cbBitsSrc = pEmr->cbBitsSrc; + uint32_t iUsageSrc = pEmr->iUsageSrc; + return(DIB_safe(record, iUsageSrc, offBmiSrc, cbBmiSrc, offBitsSrc, cbBitsSrc, blimit)); +} + +// U_EMRMASKBLT 78 +int U_EMRMASKBLT_safe(const char *record){ + if(!core5_safe(record, U_SIZE_EMRMASKBLT))return(0); + PU_EMRMASKBLT pEmr = (PU_EMRMASKBLT) (record); + const char *blimit = record + pEmr->emr.nSize; + U_OFFBMISRC offBmiSrc = pEmr->offBmiSrc; + U_CBBMISRC cbBmiSrc = pEmr->cbBmiSrc; + U_OFFBMIMSK offBmiMask = pEmr->offBmiMask; + U_CBBMIMSK cbBmiMask = pEmr->cbBmiMask; + U_OFFBITSSRC offBitsSrc = pEmr->offBitsSrc; + U_CBBITSSRC cbBitsSrc = pEmr->cbBitsSrc; + U_OFFBITSMSK offBitsMask = pEmr->offBitsMask; + U_CBBITSMSK cbBitsMask = pEmr->cbBitsMask; + uint32_t iUsageSrc = pEmr->iUsageSrc; + uint32_t iUsageMask = pEmr->iUsageMask; + if(!DIB_safe(record, iUsageSrc, offBmiSrc, cbBmiSrc, offBitsSrc, cbBitsSrc, blimit))return(0); + return(DIB_safe(record, iUsageMask, offBmiMask, cbBmiMask, offBitsMask, cbBitsMask, blimit)); +} + +// U_EMRPLGBLT 79 +int U_EMRPLGBLT_safe(const char *record){ + if(!core5_safe(record, U_SIZE_EMRPLGBLT))return(0); + PU_EMRPLGBLT pEmr = (PU_EMRPLGBLT) (record); + const char *blimit = record + pEmr->emr.nSize; + U_OFFBMISRC offBmiSrc = pEmr->offBmiSrc; + U_CBBMISRC cbBmiSrc = pEmr->cbBmiSrc; + U_OFFBMIMSK offBmiMask = pEmr->offBmiMask; + U_CBBMIMSK cbBmiMask = pEmr->cbBmiMask; + U_OFFBITSSRC offBitsSrc = pEmr->offBitsSrc; + U_CBBITSSRC cbBitsSrc = pEmr->cbBitsSrc; + U_OFFBITSMSK offBitsMask = pEmr->offBitsMask; + U_CBBITSMSK cbBitsMask = pEmr->cbBitsMask; + uint32_t iUsageSrc = pEmr->iUsageSrc; + uint32_t iUsageMask = pEmr->iUsageMask; + if(!DIB_safe(record, iUsageSrc, offBmiSrc, cbBmiSrc, offBitsSrc, cbBitsSrc, blimit))return(0); + return(DIB_safe(record, iUsageMask, offBmiMask, cbBmiMask, offBitsMask, cbBitsMask, blimit)); +} + +// U_EMRSETDIBITSTODEVICE 80 +int U_EMRSETDIBITSTODEVICE_safe(const char *record){ + if(!core5_safe(record, U_SIZE_EMRSETDIBITSTODEVICE))return(0); + PU_EMRSETDIBITSTODEVICE pEmr = (PU_EMRSETDIBITSTODEVICE) (record); + const char *blimit = record + pEmr->emr.nSize; + U_OFFBMISRC offBmiSrc = pEmr->offBmiSrc; + U_CBBMISRC cbBmiSrc = pEmr->cbBmiSrc; + U_OFFBITSSRC offBitsSrc = pEmr->offBitsSrc; + U_CBBITSSRC cbBitsSrc = pEmr->cbBitsSrc; + uint32_t iUsageSrc = pEmr->iUsageSrc; + return(DIB_safe(record, iUsageSrc, offBmiSrc, cbBmiSrc, offBitsSrc, cbBitsSrc, blimit)); +} + +// U_EMRSTRETCHDIBITS 81 +int U_EMRSTRETCHDIBITS_safe(const char *record){ + if(!core5_safe(record, U_SIZE_EMRSTRETCHDIBITS))return(0); + PU_EMRSTRETCHDIBITS pEmr = (PU_EMRSTRETCHDIBITS) (record); + const char *blimit = record + pEmr->emr.nSize; + U_OFFBMISRC offBmiSrc = pEmr->offBmiSrc; + U_CBBMISRC cbBmiSrc = pEmr->cbBmiSrc; + U_OFFBITSSRC offBitsSrc = pEmr->offBitsSrc; + U_CBBITSSRC cbBitsSrc = pEmr->cbBitsSrc; + uint32_t iUsageSrc = pEmr->iUsageSrc; + return(DIB_safe(record, iUsageSrc, offBmiSrc, cbBmiSrc, offBitsSrc, cbBitsSrc, blimit)); +} + +// U_EMREXTCREATEFONTINDIRECTW 82 +int U_EMREXTCREATEFONTINDIRECTW_safe(const char *record){ + /* Panose or logfont, LogFontExDv is not supported. Test smallest to largest */ + if(core5_safe(record, U_SIZE_EMREXTCREATEFONTINDIRECTW_LOGFONT))return(1); + return(core5_safe(record, U_SIZE_EMREXTCREATEFONTINDIRECTW_LOGFONT_PANOSE)); +} + +// U_EMREXTTEXTOUTA 83 +int U_EMREXTTEXTOUTA_safe(const char *record){ + return(core8_safe(record)); +} + +// U_EMREXTTEXTOUTW 84 +int U_EMREXTTEXTOUTW_safe(const char *record){ + return(core8_safe(record)); +} + +// U_EMRPOLYBEZIER16 85 +/** + \brief Convert a pointer to a U_EMR_POLYBEZIER16 record. + \param record pointer to a buffer holding the EMR record +*/ +int U_EMRPOLYBEZIER16_safe(const char *record){ + return(core6_safe(record)); +} + +// U_EMRPOLYGON16 86 +int U_EMRPOLYGON16_safe(const char *record){ + return(core6_safe(record)); +} + +// U_EMRPOLYLINE16 87 +int U_EMRPOLYLINE16_safe(const char *record){ + return(core6_safe(record)); +} + +// U_EMRPOLYBEZIERTO16 88 +int U_EMRPOLYBEZIERTO16_safe(const char *record){ + return(core6_safe(record)); +} + +// U_EMRPOLYLINETO16 89 +/** + \brief Convert a pointer to a U_EMR_POLYLINETO16 record. + \param record pointer to a buffer holding the EMR record +*/ +int U_EMRPOLYLINETO16_safe(const char *record){ + return(core6_safe(record)); +} + +// U_EMRPOLYPOLYLINE16 90 +int U_EMRPOLYPOLYLINE16_safe(const char *record){ + return(core10_safe(record)); +} + +// U_EMRPOLYPOLYGON16 91 +int U_EMRPOLYPOLYGON16_safe(const char *record){ + return(core10_safe(record)); +} + + +// U_EMRPOLYDRAW16 92 +int U_EMRPOLYDRAW16_safe(const char *record){ + if(!core5_safe(record, U_SIZE_EMRPOLYDRAW16))return(0); + PU_EMRPOLYDRAW16 pEmr = (PU_EMRPOLYDRAW16)(record); + int count = pEmr->cpts; + const char *blimit = record + pEmr->emr.nSize; + if(IS_MEM_UNSAFE(pEmr->apts, count*sizeof(U_POINT16), blimit))return(0); + return(1); +} + +// U_EMRCREATEMONOBRUSH 93 +int U_EMRCREATEMONOBRUSH_safe(const char *record){ + return(core12_safe(record)); +} + +// U_EMRCREATEDIBPATTERNBRUSHPT_safe 94 +int U_EMRCREATEDIBPATTERNBRUSHPT_safe(const char *record){ + return(core12_safe(record)); +} + + +// U_EMREXTCREATEPEN 95 +int U_EMREXTCREATEPEN_safe(const char *record){ + if(!core5_safe(record, U_SIZE_EMREXTCREATEPEN))return(0); + PU_EMREXTCREATEPEN pEmr = (PU_EMREXTCREATEPEN)(record); + const char *blimit = record + pEmr->emr.nSize; + U_OFFBMI offBmi = pEmr->offBmi; + U_CBBMI cbBmi = pEmr->cbBmi; + U_OFFBITS offBits = pEmr->offBits; + U_CBBITS cbBits = pEmr->cbBits; + if(!DIB_safe(record, U_DIB_RGB_COLORS, offBmi, cbBmi, offBits, cbBits, blimit))return(0); + return(extlogpen_safe((PU_EXTLOGPEN) &(pEmr->elp), blimit)); +} + +// U_EMRPOLYTEXTOUTA 96 NOT IMPLEMENTED, denigrated after Windows NT +#define U_EMRPOLYTEXTOUTA_safe(A) U_EMRNOTIMPLEMENTED_safe(A) //!< Not implemented. +// U_EMRPOLYTEXTOUTW 97 NOT IMPLEMENTED, denigrated after Windows NT +#define U_EMRPOLYTEXTOUTW_safe(A) U_EMRNOTIMPLEMENTED_safe(A) //!< Not implemented. + +// U_EMRSETICMMODE 98 +int U_EMRSETICMMODE_safe(const char *record){ + return(core3_safe(record)); +} + +// U_EMRCREATECOLORSPACE 99 +int U_EMRCREATECOLORSPACE_safe(const char *record){ + return(core5_safe(record, U_SIZE_EMRCREATECOLORSPACE)); +} + +// U_EMRSETCOLORSPACE 100 +int U_EMRSETCOLORSPACE_safe(const char *record){ + return(core3_safe(record)); +} + +// U_EMRDELETECOLORSPACE 101 +int U_EMRDELETECOLORSPACE_safe(const char *record){ + return(core3_safe(record)); +} + +// U_EMRGLSRECORD 102 Not implemented +#define U_EMRGLSRECORD_safe(A) U_EMRNOTIMPLEMENTED_safe(A) //!< Not implemented. +// U_EMRGLSBOUNDEDRECORD 103 Not implemented +#define U_EMRGLSBOUNDEDRECORD_safe(A) U_EMRNOTIMPLEMENTED_safe(A) //!< Not implemented. + +// U_EMRPIXELFORMAT 104 +int U_EMRPIXELFORMAT_safe(const char *record){ + return(core5_safe(record, U_SIZE_EMRPIXELFORMAT)); +} + +// U_EMRDRAWESCAPE 105 Not implemented +#define U_EMRDRAWESCAPE_safe(A) U_EMRNOTIMPLEMENTED_safe(A) //!< Not implemented. +// U_EMREXTESCAPE 106 Not implemented +#define U_EMREXTESCAPE_safe(A) U_EMRNOTIMPLEMENTED_safe(A) //!< Not implemented. +// U_EMRUNDEF107 107 Not implemented +#define U_EMRUNDEF107_safe(A) U_EMRNOTIMPLEMENTED_safe(A) //!< Not implemented. + +// U_EMRSMALLTEXTOUT 108 +int U_EMRSMALLTEXTOUT_safe(const char *record){ + if(!core5_safe(record, U_SIZE_EMRSMALLTEXTOUT))return(0); + PU_EMRSMALLTEXTOUT pEmr = (PU_EMRSMALLTEXTOUT)(record); + int roff=sizeof(U_EMRSMALLTEXTOUT); // offset to the start of the variable fields + int fuOptions = pEmr->fuOptions; + int cChars = pEmr->cChars; + const char *blimit = record + pEmr->emr.nSize; + if(!(fuOptions & U_ETO_NO_RECT)){ + if(IS_MEM_UNSAFE(record, roff + sizeof(U_RECTL), blimit))return(0); + } + if(IS_MEM_UNSAFE(record, roff + sizeof(U_RECTL) + cChars, blimit))return(0); + return(1); +} + +// U_EMRFORCEUFIMAPPING 109 Not implemented +#define U_EMRFORCEUFIMAPPING_safe(A) U_EMRNOTIMPLEMENTED_safe(A) //!< Not implemented. +// U_EMRNAMEDESCAPE 110 Not implemented +#define U_EMRNAMEDESCAPE_safe(A) U_EMRNOTIMPLEMENTED_safe(A) //!< Not implemented. +// U_EMRCOLORCORRECTPALETTE 111 Not implemented +#define U_EMRCOLORCORRECTPALETTE_safe(A) U_EMRNOTIMPLEMENTED_safe(A) //!< Not implemented. +// U_EMRSETICMPROFILEA 112 Not implemented +#define U_EMRSETICMPROFILEA_safe(A) U_EMRNOTIMPLEMENTED_safe(A) //!< Not implemented. +// U_EMRSETICMPROFILEW 113 Not implemented +#define U_EMRSETICMPROFILEW_safe(A) U_EMRNOTIMPLEMENTED_safe(A) //!< Not implemented. + +// U_EMRALPHABLEND 114 +int U_EMRALPHABLEND_safe(const char *record){ + return(core13_safe(record)); +} + +// U_EMRSETLAYOUT 115 +int U_EMRSETLAYOUT_safe(const char *record){ + return(core3_safe(record)); +} + +// U_EMRTRANSPARENTBLT 116 +int U_EMRTRANSPARENTBLT_safe(const char *record){ + return(core13_safe(record)); +} + + +// U_EMRUNDEF117 117 Not implemented +#define U_EMRUNDEF117_safe(A) U_EMRNOTIMPLEMENTED_safe(A) //!< Not implemented. +// U_EMRGRADIENTFILL 118 +int U_EMRGRADIENTFILL_safe(const char *record){ + if(!core5_safe(record, U_SIZE_EMRGRADIENTFILL))return(0); + PU_EMRGRADIENTFILL pEmr = (PU_EMRGRADIENTFILL)(record); + int nTriVert = pEmr->nTriVert; + int nGradObj = pEmr->nGradObj; + int ulMode = pEmr->ulMode; + const char *blimit = record + pEmr->emr.nSize; + if(IS_MEM_UNSAFE(record, nTriVert*sizeof(U_TRIVERTEX), blimit))return(0); + record += nTriVert * sizeof(U_TRIVERTEX); + if(nGradObj){ + if( ulMode == U_GRADIENT_FILL_TRIANGLE){ + if(IS_MEM_UNSAFE(record, nGradObj*sizeof(U_GRADIENT3), blimit))return(0); + } + else if(ulMode == U_GRADIENT_FILL_RECT_H || ulMode == U_GRADIENT_FILL_RECT_V){ + if(IS_MEM_UNSAFE(record, nGradObj*sizeof(U_GRADIENT4), blimit))return(0); + } + } + return(1); +} + +// U_EMRSETLINKEDUFIS 119 Not implemented +#define U_EMRSETLINKEDUFIS_safe(A) U_EMRNOTIMPLEMENTED_safe(A) //!< Not implemented. +// U_EMRSETTEXTJUSTIFICATION120 Not implemented (denigrated) +#define U_EMRSETTEXTJUSTIFICATION_safe(A) U_EMRNOTIMPLEMENTED_safe(A) //!< Not implemented. +// U_EMRCOLORMATCHTOTARGETW 121 Not implemented +#define U_EMRCOLORMATCHTOTARGETW_safe(A) U_EMRNOTIMPLEMENTED_safe(A) //!< Not implemented. + +// U_EMRCREATECOLORSPACEW 122 +int U_EMRCREATECOLORSPACEW_safe(const char *record){ + return(core5_safe(record, U_SIZE_EMRCREATECOLORSPACEW)); +} + +//! \endcond + +/** + \brief Test an EMF record in memory from Big Endian to Little Endian. + \return 0 on failure, 1 on success + \param record pointer to the EMF record in memory + + Normally this would be called immediately after reading a record from a file + and having called U_emf_record_sizeok(). + It is NOT safe to call this routine without first calling U_emf_record_sizeok)()! + If the file has been converted from one endian to another calling this routine is + not necessary, because those routines also perform these checks. +*/ +int U_emf_record_safe(const char *record){ + int rstatus=1; + + if(!record)return(0); // programming error + + switch (U_EMRTYPE(record)) + { + case U_EMR_HEADER: rstatus=U_EMRHEADER_safe(record); break; + case U_EMR_POLYBEZIER: rstatus=U_EMRPOLYBEZIER_safe(record); break; + case U_EMR_POLYGON: rstatus=U_EMRPOLYGON_safe(record); break; + case U_EMR_POLYLINE: rstatus=U_EMRPOLYLINE_safe(record); break; + case U_EMR_POLYBEZIERTO: rstatus=U_EMRPOLYBEZIERTO_safe(record); break; + case U_EMR_POLYLINETO: rstatus=U_EMRPOLYLINETO_safe(record); break; + case U_EMR_POLYPOLYLINE: rstatus=U_EMRPOLYPOLYLINE_safe(record); break; + case U_EMR_POLYPOLYGON: rstatus=U_EMRPOLYPOLYGON_safe(record); break; + case U_EMR_SETWINDOWEXTEX: rstatus=U_EMRSETWINDOWEXTEX_safe(record); break; + case U_EMR_SETWINDOWORGEX: rstatus=U_EMRSETWINDOWORGEX_safe(record); break; + case U_EMR_SETVIEWPORTEXTEX: rstatus=U_EMRSETVIEWPORTEXTEX_safe(record); break; + case U_EMR_SETVIEWPORTORGEX: rstatus=U_EMRSETVIEWPORTORGEX_safe(record); break; + case U_EMR_SETBRUSHORGEX: rstatus=U_EMRSETBRUSHORGEX_safe(record); break; + case U_EMR_EOF: rstatus=U_EMREOF_safe(record); break; + case U_EMR_SETPIXELV: rstatus=U_EMRSETPIXELV_safe(record); break; + case U_EMR_SETMAPPERFLAGS: rstatus=U_EMRSETMAPPERFLAGS_safe(record); break; + case U_EMR_SETMAPMODE: rstatus=U_EMRSETMAPMODE_safe(record); break; + case U_EMR_SETBKMODE: rstatus=U_EMRSETBKMODE_safe(record); break; + case U_EMR_SETPOLYFILLMODE: rstatus=U_EMRSETPOLYFILLMODE_safe(record); break; + case U_EMR_SETROP2: rstatus=U_EMRSETROP2_safe(record); break; + case U_EMR_SETSTRETCHBLTMODE: rstatus=U_EMRSETSTRETCHBLTMODE_safe(record); break; + case U_EMR_SETTEXTALIGN: rstatus=U_EMRSETTEXTALIGN_safe(record); break; + case U_EMR_SETCOLORADJUSTMENT: rstatus=U_EMRSETCOLORADJUSTMENT_safe(record); break; + case U_EMR_SETTEXTCOLOR: rstatus=U_EMRSETTEXTCOLOR_safe(record); break; + case U_EMR_SETBKCOLOR: rstatus=U_EMRSETBKCOLOR_safe(record); break; + case U_EMR_OFFSETCLIPRGN: rstatus=U_EMROFFSETCLIPRGN_safe(record); break; + case U_EMR_MOVETOEX: rstatus=U_EMRMOVETOEX_safe(record); break; + case U_EMR_SETMETARGN: rstatus=U_EMRSETMETARGN_safe(record); break; + case U_EMR_EXCLUDECLIPRECT: rstatus=U_EMREXCLUDECLIPRECT_safe(record); break; + case U_EMR_INTERSECTCLIPRECT: rstatus=U_EMRINTERSECTCLIPRECT_safe(record); break; + case U_EMR_SCALEVIEWPORTEXTEX: rstatus=U_EMRSCALEVIEWPORTEXTEX_safe(record); break; + case U_EMR_SCALEWINDOWEXTEX: rstatus=U_EMRSCALEWINDOWEXTEX_safe(record); break; + case U_EMR_SAVEDC: rstatus=U_EMRSAVEDC_safe(record); break; + case U_EMR_RESTOREDC: rstatus=U_EMRRESTOREDC_safe(record); break; + case U_EMR_SETWORLDTRANSFORM: rstatus=U_EMRSETWORLDTRANSFORM_safe(record); break; + case U_EMR_MODIFYWORLDTRANSFORM: rstatus=U_EMRMODIFYWORLDTRANSFORM_safe(record); break; + case U_EMR_SELECTOBJECT: rstatus=U_EMRSELECTOBJECT_safe(record); break; + case U_EMR_CREATEPEN: rstatus=U_EMRCREATEPEN_safe(record); break; + case U_EMR_CREATEBRUSHINDIRECT: rstatus=U_EMRCREATEBRUSHINDIRECT_safe(record); break; + case U_EMR_DELETEOBJECT: rstatus=U_EMRDELETEOBJECT_safe(record); break; + case U_EMR_ANGLEARC: rstatus=U_EMRANGLEARC_safe(record); break; + case U_EMR_ELLIPSE: rstatus=U_EMRELLIPSE_safe(record); break; + case U_EMR_RECTANGLE: rstatus=U_EMRRECTANGLE_safe(record); break; + case U_EMR_ROUNDRECT: rstatus=U_EMRROUNDRECT_safe(record); break; + case U_EMR_ARC: rstatus=U_EMRARC_safe(record); break; + case U_EMR_CHORD: rstatus=U_EMRCHORD_safe(record); break; + case U_EMR_PIE: rstatus=U_EMRPIE_safe(record); break; + case U_EMR_SELECTPALETTE: rstatus=U_EMRSELECTPALETTE_safe(record); break; + case U_EMR_CREATEPALETTE: rstatus=U_EMRCREATEPALETTE_safe(record); break; + case U_EMR_SETPALETTEENTRIES: rstatus=U_EMRSETPALETTEENTRIES_safe(record); break; + case U_EMR_RESIZEPALETTE: rstatus=U_EMRRESIZEPALETTE_safe(record); break; + case U_EMR_REALIZEPALETTE: rstatus=U_EMRREALIZEPALETTE_safe(record); break; + case U_EMR_EXTFLOODFILL: rstatus=U_EMREXTFLOODFILL_safe(record); break; + case U_EMR_LINETO: rstatus=U_EMRLINETO_safe(record); break; + case U_EMR_ARCTO: rstatus=U_EMRARCTO_safe(record); break; + case U_EMR_POLYDRAW: rstatus=U_EMRPOLYDRAW_safe(record); break; + case U_EMR_SETARCDIRECTION: rstatus=U_EMRSETARCDIRECTION_safe(record); break; + case U_EMR_SETMITERLIMIT: rstatus=U_EMRSETMITERLIMIT_safe(record); break; + case U_EMR_BEGINPATH: rstatus=U_EMRBEGINPATH_safe(record); break; + case U_EMR_ENDPATH: rstatus=U_EMRENDPATH_safe(record); break; + case U_EMR_CLOSEFIGURE: rstatus=U_EMRCLOSEFIGURE_safe(record); break; + case U_EMR_FILLPATH: rstatus=U_EMRFILLPATH_safe(record); break; + case U_EMR_STROKEANDFILLPATH: rstatus=U_EMRSTROKEANDFILLPATH_safe(record); break; + case U_EMR_STROKEPATH: rstatus=U_EMRSTROKEPATH_safe(record); break; + case U_EMR_FLATTENPATH: rstatus=U_EMRFLATTENPATH_safe(record); break; + case U_EMR_WIDENPATH: rstatus=U_EMRWIDENPATH_safe(record); break; + case U_EMR_SELECTCLIPPATH: rstatus=U_EMRSELECTCLIPPATH_safe(record); break; + case U_EMR_ABORTPATH: rstatus=U_EMRABORTPATH_safe(record); break; + case U_EMR_UNDEF69: rstatus=U_EMRUNDEF69_safe(record); break; + case U_EMR_COMMENT: rstatus=U_EMRCOMMENT_safe(record); break; + case U_EMR_FILLRGN: rstatus=U_EMRFILLRGN_safe(record); break; + case U_EMR_FRAMERGN: rstatus=U_EMRFRAMERGN_safe(record); break; + case U_EMR_INVERTRGN: rstatus=U_EMRINVERTRGN_safe(record); break; + case U_EMR_PAINTRGN: rstatus=U_EMRPAINTRGN_safe(record); break; + case U_EMR_EXTSELECTCLIPRGN: rstatus=U_EMREXTSELECTCLIPRGN_safe(record); break; + case U_EMR_BITBLT: rstatus=U_EMRBITBLT_safe(record); break; + case U_EMR_STRETCHBLT: rstatus=U_EMRSTRETCHBLT_safe(record); break; + case U_EMR_MASKBLT: rstatus=U_EMRMASKBLT_safe(record); break; + case U_EMR_PLGBLT: rstatus=U_EMRPLGBLT_safe(record); break; + case U_EMR_SETDIBITSTODEVICE: rstatus=U_EMRSETDIBITSTODEVICE_safe(record); break; + case U_EMR_STRETCHDIBITS: rstatus=U_EMRSTRETCHDIBITS_safe(record); break; + case U_EMR_EXTCREATEFONTINDIRECTW: rstatus=U_EMREXTCREATEFONTINDIRECTW_safe(record); break; + case U_EMR_EXTTEXTOUTA: rstatus=U_EMREXTTEXTOUTA_safe(record); break; + case U_EMR_EXTTEXTOUTW: rstatus=U_EMREXTTEXTOUTW_safe(record); break; + case U_EMR_POLYBEZIER16: rstatus=U_EMRPOLYBEZIER16_safe(record); break; + case U_EMR_POLYGON16: rstatus=U_EMRPOLYGON16_safe(record); break; + case U_EMR_POLYLINE16: rstatus=U_EMRPOLYLINE16_safe(record); break; + case U_EMR_POLYBEZIERTO16: rstatus=U_EMRPOLYBEZIERTO16_safe(record); break; + case U_EMR_POLYLINETO16: rstatus=U_EMRPOLYLINETO16_safe(record); break; + case U_EMR_POLYPOLYLINE16: rstatus=U_EMRPOLYPOLYLINE16_safe(record); break; + case U_EMR_POLYPOLYGON16: rstatus=U_EMRPOLYPOLYGON16_safe(record); break; + case U_EMR_POLYDRAW16: rstatus=U_EMRPOLYDRAW16_safe(record); break; + case U_EMR_CREATEMONOBRUSH: rstatus=U_EMRCREATEMONOBRUSH_safe(record); break; + case U_EMR_CREATEDIBPATTERNBRUSHPT: rstatus=U_EMRCREATEDIBPATTERNBRUSHPT_safe(record); break; + case U_EMR_EXTCREATEPEN: rstatus=U_EMREXTCREATEPEN_safe(record); break; + case U_EMR_POLYTEXTOUTA: rstatus=U_EMRPOLYTEXTOUTA_safe(record); break; + case U_EMR_POLYTEXTOUTW: rstatus=U_EMRPOLYTEXTOUTW_safe(record); break; + case U_EMR_SETICMMODE: rstatus=U_EMRSETICMMODE_safe(record); break; + case U_EMR_CREATECOLORSPACE: rstatus=U_EMRCREATECOLORSPACE_safe(record); break; + case U_EMR_SETCOLORSPACE: rstatus=U_EMRSETCOLORSPACE_safe(record); break; + case U_EMR_DELETECOLORSPACE: rstatus=U_EMRDELETECOLORSPACE_safe(record); break; + case U_EMR_GLSRECORD: rstatus=U_EMRGLSRECORD_safe(record); break; + case U_EMR_GLSBOUNDEDRECORD: rstatus=U_EMRGLSBOUNDEDRECORD_safe(record); break; + case U_EMR_PIXELFORMAT: rstatus=U_EMRPIXELFORMAT_safe(record); break; + case U_EMR_DRAWESCAPE: rstatus=U_EMRDRAWESCAPE_safe(record); break; + case U_EMR_EXTESCAPE: rstatus=U_EMREXTESCAPE_safe(record); break; + case U_EMR_UNDEF107: rstatus=U_EMRUNDEF107_safe(record); break; + case U_EMR_SMALLTEXTOUT: rstatus=U_EMRSMALLTEXTOUT_safe(record); break; + case U_EMR_FORCEUFIMAPPING: rstatus=U_EMRFORCEUFIMAPPING_safe(record); break; + case U_EMR_NAMEDESCAPE: rstatus=U_EMRNAMEDESCAPE_safe(record); break; + case U_EMR_COLORCORRECTPALETTE: rstatus=U_EMRCOLORCORRECTPALETTE_safe(record); break; + case U_EMR_SETICMPROFILEA: rstatus=U_EMRSETICMPROFILEA_safe(record); break; + case U_EMR_SETICMPROFILEW: rstatus=U_EMRSETICMPROFILEW_safe(record); break; + case U_EMR_ALPHABLEND: rstatus=U_EMRALPHABLEND_safe(record); break; + case U_EMR_SETLAYOUT: rstatus=U_EMRSETLAYOUT_safe(record); break; + case U_EMR_TRANSPARENTBLT: rstatus=U_EMRTRANSPARENTBLT_safe(record); break; + case U_EMR_UNDEF117: rstatus=U_EMRUNDEF117_safe(record); break; + case U_EMR_GRADIENTFILL: rstatus=U_EMRGRADIENTFILL_safe(record); break; + case U_EMR_SETLINKEDUFIS: rstatus=U_EMRSETLINKEDUFIS_safe(record); break; + case U_EMR_SETTEXTJUSTIFICATION: rstatus=U_EMRSETTEXTJUSTIFICATION_safe(record); break; + case U_EMR_COLORMATCHTOTARGETW: rstatus=U_EMRCOLORMATCHTOTARGETW_safe(record); break; + case U_EMR_CREATECOLORSPACEW: rstatus=U_EMRCREATECOLORSPACEW_safe(record); break; + default: rstatus=U_EMRNOTIMPLEMENTED_safe(record); break; + } //end of switch + return(rstatus); +} + + +#ifdef __cplusplus +} +#endif diff --git a/src/libuemf/uemf_safe.h b/src/libuemf/uemf_safe.h new file mode 100644 index 000000000..0c9e3cc69 --- /dev/null +++ b/src/libuemf/uemf_safe.h @@ -0,0 +1,32 @@ +/** + @file uemf_safe.h + + @brief Defintions and prototype for function for converting EMF records between Big Endian and Little Endian byte orders. +*/ + +/* +File: uemf_endian.h +Version: 0.0.2 +Date: 26-MAR-2015 +Author: David Mathog, Biology Division, Caltech +email: mathog@caltech.edu +Copyright: 2015 David Mathog and California Institute of Technology (Caltech) +*/ + +#ifndef _UEMF_SAFE_ +#define _UEMF_SAFE_ + +#ifdef __cplusplus +extern "C" { +#endif + +// prototypes +int U_emf_record_safe(const char *record); +int bitmapinfo_safe(const char *Bmi, const char *blimit); +//! \endcond + +#ifdef __cplusplus +} +#endif + +#endif /* _UEMF_SAFE_ */ diff --git a/src/libuemf/upmf.c b/src/libuemf/upmf.c index 01f7ba3a4..fe929e443 100644 --- a/src/libuemf/upmf.c +++ b/src/libuemf/upmf.c @@ -21,11 +21,11 @@ /* File: upmf.c -Version: 0.0.6 -Date: 26-MAR-2014 +Version: 0.0.11 +Date: 28-MAY-2015 Author: David Mathog, Biology Division, Caltech email: mathog@caltech.edu -Copyright: 2014 David Mathog and California Institute of Technology (Caltech) +Copyright: 2015 David Mathog and California Institute of Technology (Caltech) */ #ifdef __cplusplus @@ -1459,11 +1459,18 @@ int U_PMF_PTRSAV_COND(const char **Dst, const char *Src, int Doit){ else { *Dst = NULL; } return(1); } +/* + + ===================================================================================== + start of U_PMF_*_get() functions + +*/ /** \brief Get the 16 bit unsigned Flags field from a header. \param contents Record from which to extract data, will be incremented by header size. \return Flags field + In many records the only value needed from the header is Flags. Rather than mapping the entire Header and returning it, in these instances this function may be called to just get this one value. @@ -1516,6 +1523,19 @@ int U_PMF_CMN_HDR_get(const char **contents, U_PMF_CMN_HDR *Header){ } /** + \brief return the size in bytes of the EMF+ record + \param contents Record from which to extract data, will not be modified. + \returns size, or 0 if contents is Null +*/ +int U_PMF_RECORD_SIZE_get(const char *contents){ + if(!contents){ return(0); } + int Size; + const char *from = contents + 4; + U_PMF_SERIAL_get(&from, &Size, 4, 1, U_LE); + return(Size); +} + +/** \brief Return the size of a PenData object from an EMF+ record. \param PenData Address in memory where the PenData object starts. \returns size of the object in bytes @@ -3057,7 +3077,7 @@ U_PSEUDO_OBJ *U_PMF_POINTR_set(uint32_t Elements, const U_PMF_POINTF *Coords){ poi = U_PMF_4NUM_set(Elements); po = U_PO_append(po, poi->Data, poi->Used); U_PO_free(&poi); - if(po)goto end; + if(!po)goto end; for(Xf = Yf = 0.0 ;Elements; Elements--, Coords++){ Xf = U_ROUND(Coords->X) - Xf; @@ -5362,6 +5382,17 @@ U_PSEUDO_OBJ *U_PMR_TRANSLATEWORLDTRANSFORM_set(int xmtype, U_FLOAT Dx, U_FLOAT return(po); } +/* + + end of U_PMF_*_set() functions + ===================================================================================== + start of U_PMF_*_get() functions + + These functions all take a blimit value so that they can check if the data description in the fields + they process extend beyond the end of the record. + +*/ + //! \cond /* core _get functions, not accessed outside of this routine */ @@ -5387,11 +5418,15 @@ int U_PMF_CORE1_get(const char *contents, void *v1, void *v2, void *v3, void *v4 \param Version EmfPlusGraphicsVersion object \param Type BrushType Enumeration \param Data one of the 5 types of Brush data + \param blimit one byte past the end of data EMF+ manual 2.2.1.1, Microsoft name: EmfPlusBrush Object + + Caller must check Data for possible memory access violations. */ -int U_PMF_BRUSH_get(const char *contents, uint32_t *Version, uint32_t *Type, const char **Data){ - if(!contents || !Version || !Type || !Data){ return(0); } +int U_PMF_BRUSH_get(const char *contents, uint32_t *Version, uint32_t *Type, const char **Data, const char *blimit){ + if(!contents || !Version || !Type || !Data || !blimit){ return(0); } + if(IS_MEM_UNSAFE(contents,sizeof(U_PMF_BRUSH), blimit))return(0); U_PMF_SERIAL_get(&contents, Version, 4, 1, U_LE); U_PMF_SERIAL_get(&contents, Type, 4, 1, U_LE); U_PMF_PTRSAV_SHIFT(Data, &contents, 0); @@ -5405,11 +5440,15 @@ int U_PMF_BRUSH_get(const char *contents, uint32_t *Version, uint32_t *Type, con \param Version EmfPlusGraphicsVersion object \param Type CustomLineCapData Enumeration \param Data one of the 2 types of Linecap data + \param blimit one byte past the end of data EMF+ manual 2.2.1.2, Microsoft name: EmfPlusCustomLineCap Object + + Caller must check Data for possible memory access violations. */ -int U_PMF_CUSTOMLINECAP_get(const char *contents, uint32_t *Version, uint32_t *Type, const char **Data){ - if(!contents || !Version || !Type || !Data){ return(0); } +int U_PMF_CUSTOMLINECAP_get(const char *contents, uint32_t *Version, uint32_t *Type, const char **Data, const char *blimit){ + if(!contents || !Version || !Type || !Data || !blimit){ return(0); } + if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_CUSTOMLINECAP), blimit))return(0); U_PMF_SERIAL_get(&contents, Version, 4, 1, U_LE); U_PMF_SERIAL_get(&contents, Type, 4, 1, U_LE); U_PMF_PTRSAV_SHIFT(Data, &contents, 0); @@ -5428,10 +5467,13 @@ int U_PMF_CUSTOMLINECAP_get(const char *contents, uint32_t *Version, uint32_t *T \param Data Unicode (UTF-16LE) name of font family EMF+ manual 2.2.1.3, Microsoft name: EmfPlusFont Object + + Caller must check Data for possible memory access violations. */ int U_PMF_FONT_get(const char *contents, uint32_t *Version, U_FLOAT *EmSize, uint32_t *SizeUnit, - int32_t *FSFlags, uint32_t *Length, const char **Data){ - if(!contents || !Version || !EmSize || !SizeUnit || !FSFlags || !Length || !Data){ return(0); } + int32_t *FSFlags, uint32_t *Length, const char **Data, const char *blimit){ + if(!contents || !Version || !EmSize || !SizeUnit || !FSFlags || !Length || !Data || !blimit){ return(0); } + if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_FONT), blimit))return(0); U_PMF_SERIAL_get(&contents, Version, 4, 1, U_LE); U_PMF_SERIAL_get(&contents, EmSize, 4, 1, U_LE); U_PMF_SERIAL_get(&contents, SizeUnit, 4, 1, U_LE); @@ -5452,9 +5494,12 @@ int U_PMF_FONT_get(const char *contents, uint32_t *Version, U_FLOAT *EmSize, uin \param Data one of the 2 types of image data EMF+ manual 2.2.1.4, Microsoft name: EmfPlusImage Object + + Caller must check Data for possible memory access violations. */ -int U_PMF_IMAGE_get(const char *contents, uint32_t *Version, uint32_t *Type, const char **Data){ - if(!contents || !Version || !Type){ return(0); } +int U_PMF_IMAGE_get(const char *contents, uint32_t *Version, uint32_t *Type, const char **Data, const char *blimit){ + if(!contents || !Version || !Type || !blimit){ return(0); } + if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_IMAGE), blimit))return(0); U_PMF_SERIAL_get(&contents, Version, 4, 1, U_LE); U_PMF_SERIAL_get(&contents, Type, 4, 1, U_LE); U_PMF_PTRSAV_SHIFT(Data, &contents, 0); @@ -5472,8 +5517,10 @@ int U_PMF_IMAGE_get(const char *contents, uint32_t *Version, uint32_t *Type, con EMF+ manual 2.2.1.5, Microsoft name: EmfPlusImageAttributes Object */ -int U_PMF_IMAGEATTRIBUTES_get(const char *contents, uint32_t *Version, uint32_t *WrapMode, uint32_t *ClampColor, uint32_t *ObjectClamp){ - if(!contents || !Version || !WrapMode || !ClampColor || !ObjectClamp){ return(0); } +int U_PMF_IMAGEATTRIBUTES_get(const char *contents, uint32_t *Version, uint32_t *WrapMode, uint32_t *ClampColor, + uint32_t *ObjectClamp, const char *blimit){ + if(!contents || !Version || !WrapMode || !ClampColor || !ObjectClamp || !blimit){ return(0); } + if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_IMAGEATTRIBUTES), blimit))return(0); U_PMF_SERIAL_get(&contents, Version, 4, 1, U_LE); contents += 4; /* Skip Reserved 1*/ U_PMF_SERIAL_get(&contents, WrapMode, 4, 1, U_LE); @@ -5494,21 +5541,31 @@ int U_PMF_IMAGEATTRIBUTES_get(const char *contents, uint32_t *Version, uint32_t \param Types array of U_PMF_PATHPOINTTYPERLE and/or U_PMF_PATHPOINTTYPE EMF+ manual 2.2.1.6, Microsoft name: EmfPlusPath Object + + Caller must check Types for possible memory access violations if type can be U_PMF_PATHPOINTTYPERLE. */ -int U_PMF_PATH_get(const char *contents, uint32_t *Version, uint32_t *Count, uint16_t *Flags, const char **Points, const char **Types){ - if(!contents || !Version || !Count || !Flags || !Points || !Types){ return(0); } +int U_PMF_PATH_get(const char *contents, uint32_t *Version, uint32_t *Count, uint16_t *Flags, + const char **Points, const char **Types, const char *blimit){ + if(!contents || !Version || !Count || !Flags || !Points || !Types || !blimit){ return(0); } + if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_PATH), blimit))return(0); U_PMF_SERIAL_get(&contents, Version, 4, 1, U_LE); U_PMF_SERIAL_get(&contents, Count, 4, 1, U_LE); U_PMF_SERIAL_get(&contents, Flags, 2, 1, U_LE); contents+=2; /* reserved */ - U_PMF_PTRSAV_SHIFT(Points, &contents, 0); + uint32_t sizeP, sizeT; if(*Flags & U_PPF_P){ - int killme = U_PMF_LEN_REL715(contents,*Count); //DEBUG - printf("DEBUG U_PMF_PATH_get count:%d LENREL715:%d\n",*Count,killme);fflush(stdout); - contents += killme; + sizeP = U_PMF_LEN_REL715(contents,*Count); //DEBUG + printf("DEBUG U_PMF_PATH_get count:%d LENREL715:%d\n",*Count,sizeP);fflush(stdout); } - else if(*Flags & U_PPF_C){ contents += *Count * sizeof(U_PMF_POINT); } - else { contents += *Count * sizeof(U_PMF_POINTF); } + else if(*Flags & U_PPF_C){ sizeP = *Count * sizeof(U_PMF_POINT); } + else { sizeP = *Count * sizeof(U_PMF_POINTF); } + if(IS_MEM_UNSAFE(contents, sizeP, blimit))return(0); + U_PMF_PTRSAV_SHIFT(Points, &contents, 0); + contents += sizeP; + /* this limit is correct if there are only U_PMF_PATHPOINTTYPE PointTypes, it is a lower bound if + there can also be U_PMF_PATHPOINTTYPERLE */ + sizeT = *Count * sizeof(U_PMF_PATHPOINTTYPE); + if(IS_MEM_UNSAFE(contents, sizeT, blimit))return(0); U_PMF_PTRSAV_SHIFT(Types, &contents, 0); return(1); } @@ -5523,9 +5580,12 @@ int U_PMF_PATH_get(const char *contents, uint32_t *Version, uint32_t *Count, uin \param Brush Brush Description EMF+ manual 2.2.1.7, Microsoft name: EmfPlusPen Object + + Caller must check Brush and PenData for possible memory access violations. */ -int U_PMF_PEN_get(const char *contents, uint32_t *Version, uint32_t *Type, const char **PenData, const char **Brush){ - if(!contents || !Type || !PenData || !Brush){ return(0); } +int U_PMF_PEN_get(const char *contents, uint32_t *Version, uint32_t *Type, const char **PenData, const char **Brush, const char *blimit){ + if(!contents || !Type || !PenData || !Brush || !blimit){ return(0); } + if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_PEN), blimit))return(0); U_PMF_SERIAL_get(&contents, Version, 4, 1, U_LE); U_PMF_SERIAL_get(&contents, Type, 4, 1, U_LE); U_PMF_PTRSAV_SHIFT(PenData, &contents, 0); @@ -5543,8 +5603,9 @@ int U_PMF_PEN_get(const char *contents, uint32_t *Version, uint32_t *Type, const EMF+ manual 2.2.1.8, Microsoft name: EmfPlusRegion Object */ -int U_PMF_REGION_get(const char *contents, uint32_t *Version, uint32_t *Count, const char **Nodes){ - if(!contents || !Version || !Count || !Nodes){ return(0); } +int U_PMF_REGION_get(const char *contents, uint32_t *Version, uint32_t *Count, const char **Nodes, const char *blimit){ + if(!contents || !Version || !Count || !Nodes || !blimit){ return(0); } + if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_REGION), blimit))return(0); U_PMF_SERIAL_get(&contents, Version, 4, 1, U_LE); U_PMF_SERIAL_get(&contents, Count, 4, 1, U_LE); U_PMF_PTRSAV_SHIFT(Nodes, &contents, 0); @@ -5560,8 +5621,9 @@ int U_PMF_REGION_get(const char *contents, uint32_t *Version, uint32_t *Count, c EMF+ manual 2.2.1.9, Microsoft name: EmfPlusStringFormat Object */ -int U_PMF_STRINGFORMAT_get(const char *contents, U_PMF_STRINGFORMAT *Sfs, const char **Data){ - if(!contents || !Sfs || !Data){ return(0); } +int U_PMF_STRINGFORMAT_get(const char *contents, U_PMF_STRINGFORMAT *Sfs, const char **Data, const char *blimit){ + if(!contents || !Sfs || !Data || !blimit){ return(0); } + if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_STRINGFORMAT), blimit))return(0); U_PMF_SERIAL_get(&contents, Sfs, 4, 15, U_LE); *Data = contents; return(1); @@ -5578,8 +5640,9 @@ int U_PMF_STRINGFORMAT_get(const char *contents, U_PMF_STRINGFORMAT *Sfs, const EMF+ manual 2.2.2.1, Microsoft name: EmfPlusARGB Object */ -int U_PMF_ARGB_get(const char *contents, uint8_t *Blue, uint8_t *Green, uint8_t *Red, uint8_t *Alpha){ - if(!contents || !Blue || !Green || !Red || !Alpha){ return(0); } +int U_PMF_ARGB_get(const char *contents, uint8_t *Blue, uint8_t *Green, uint8_t *Red, uint8_t *Alpha, const char *blimit){ + if(!contents || !Blue || !Green || !Red || !Alpha || !blimit){ return(0); } + if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_ARGB), blimit))return(0); U_PMF_SERIAL_get(&contents, Blue, 1, 1, U_XE); U_PMF_SERIAL_get(&contents, Green, 1, 1, U_XE); U_PMF_SERIAL_get(&contents, Red, 1, 1, U_XE); @@ -5595,11 +5658,14 @@ int U_PMF_ARGB_get(const char *contents, uint8_t *Blue, uint8_t *Green, uint8_t \param Data pointer to variable part EMF+ manual 2.2.2.2, Microsoft name: EmfPlusBitmap Object + + Caller must check Data for possible memory access violations. */ -int U_PMF_BITMAP_get(const char *contents, U_PMF_BITMAP *Bs, const char **Data){ - if(!contents || !Bs || !Data){ return(0); } - U_PMF_SERIAL_get(&contents, Bs, 4, 5, U_LE); - U_PMF_PTRSAV_SHIFT(Data, &contents, 0); +int U_PMF_BITMAP_get(const char *contents, U_PMF_BITMAP *Bs, const char **Data, const char *blimit){ + if(!contents || !Bs || !Data || !blimit){ return(0); } + if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_BITMAP), blimit))return(0); + U_PMF_SERIAL_get(&contents, Bs, 4, 5, U_LE); // width, height, stride, pixelformat, type + U_PMF_PTRSAV_SHIFT(Data, &contents, 0); // bitmapdata return(1); } @@ -5612,11 +5678,16 @@ int U_PMF_BITMAP_get(const char *contents, U_PMF_BITMAP *Bs, const char **Data){ \param Data An array of bytes, meaning depends on fields in U_PMF_BITMAP object and the PixelFormat enumeration. EMF+ manual 2.2.2.3, Microsoft name: EmfPlusBitmapData Object + + Caller must check Data for possible memory access violations. */ -int U_PMF_BITMAPDATA_get(const char *contents, U_PMF_PALETTE *Ps, const char **Colors, const char **Data){ - if(!contents || !Ps || !Colors || !Data ){ return(0); } +int U_PMF_BITMAPDATA_get(const char *contents, U_PMF_PALETTE *Ps, const char **Colors, const char **Data, const char *blimit){ + if(!contents || !Ps || !Colors || !Data || !blimit){ return(0); } + /* this structure is entirely optional */ + if(IS_MEM_UNSAFE(contents, 4*2, blimit))return(0); U_PMF_SERIAL_get(&contents, Ps, 4, 2, U_LE); U_PMF_PTRSAV_SHIFT(Colors, &contents, Ps->Elements * sizeof(U_PMF_ARGB)); + /* difficult to know how big the actual bitmap will be, just return the pointer to it untested */ U_PMF_PTRSAV_SHIFT(Data, &contents, 0); return(1); } @@ -5627,13 +5698,15 @@ int U_PMF_BITMAPDATA_get(const char *contents, U_PMF_PALETTE *Ps, const char **C \param contents Record from which to extract data \param Elements Number of members in Positions and Colors \param Positions Caller must free. Pointer to memory holding positions along gradient line. - \param Colors Caller must NOT free memory ,Pointer to memory holding colors at positions on gradient line. + \param Colors Caller must NOT free memory, Pointer to memory holding colors at positions on gradient line. EMF+ manual 2.2.2.4, Microsoft name: EmfPlusBlendColors Object */ -int U_PMF_BLENDCOLORS_get(const char *contents, uint32_t *Elements, U_FLOAT **Positions, const char **Colors){ - if(!contents || !Positions || !Colors){ return(0); } +int U_PMF_BLENDCOLORS_get(const char *contents, uint32_t *Elements, U_FLOAT **Positions, const char **Colors, const char *blimit){ + if(!contents || !Positions || !Colors || !blimit){ return(0); } + if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_BLENDCOLORS), blimit))return(0); U_PMF_SERIAL_get(&contents, Elements, 4, 1, U_LE); + if(IS_MEM_UNSAFE(contents, *Elements * 4, blimit))return(0); if(!U_PMF_SERIAL_array_copy_get(&contents, (void **)Positions, 4, *Elements, U_LE,1)){ return(0); } U_PMF_PTRSAV_SHIFT(Colors, &contents, 0); return(1); @@ -5649,9 +5722,11 @@ int U_PMF_BLENDCOLORS_get(const char *contents, uint32_t *Elements, U_FLOAT **Po EMF+ manual 2.2.2.5, Microsoft name: EmfPlusBlendFactors Object */ -int U_PMF_BLENDFACTORS_get(const char *contents, uint32_t *Elements, U_FLOAT **Positions, U_FLOAT **Factors){ - if(!contents || !Elements || !Positions || !Factors){ return(0); } +int U_PMF_BLENDFACTORS_get(const char *contents, uint32_t *Elements, U_FLOAT **Positions, U_FLOAT **Factors, const char *blimit){ + if(!contents || !Elements || !Positions || !Factors || !blimit){ return(0); } + if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_BLENDFACTORS), blimit))return(0); U_PMF_SERIAL_get(&contents, Elements, 4, 1, U_LE); + if(IS_MEM_UNSAFE(contents, *Elements * 4 * 2, blimit))return(0); if(!U_PMF_SERIAL_array_copy_get(&contents, (void **)Positions, 4, *Elements, U_LE, 1)){ return(0); } if(!U_PMF_SERIAL_array_copy_get(&contents, (void **)Factors, 4, *Elements, U_LE, 1)){ free(*Positions); @@ -5668,9 +5743,12 @@ int U_PMF_BLENDFACTORS_get(const char *contents, uint32_t *Elements, U_FLOAT **P \param Data boundary of the brush EMF+ manual 2.2.2.6, Microsoft name: EmfPlusBoundaryPathData Object + + Caller must check Data for possible memory access violations. */ -int U_PMF_BOUNDARYPATHDATA_get(const char *contents, int32_t *Size, const char **Data){ - if(!contents || !Size || !Data){ return(0); } +int U_PMF_BOUNDARYPATHDATA_get(const char *contents, int32_t *Size, const char **Data, const char *blimit){ + if(!contents || !Size || !Data || !blimit){ return(0); } + if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_BOUNDARYPATHDATA), blimit))return(0); U_PMF_SERIAL_get(&contents, Size, 4, 1, U_LE); U_PMF_PTRSAV_SHIFT(Data, &contents, 0); return(1); @@ -5680,14 +5758,16 @@ int U_PMF_BOUNDARYPATHDATA_get(const char *contents, int32_t *Size, const char * \brief Get data from a U_PMF_BOUNDARYPOINTDATA object \return 1 on success, 0 on error \param contents Record from which to extract data - \param Elements Members in POints + \param Elements Members in Points \param Points Caller must free. Pointer to memory holding points along gradient line. Boundary of the brush. EMF+ manual 2.2.2.7, Microsoft name: EmfPlusBoundaryPointData Object */ -int U_PMF_BOUNDARYPOINTDATA_get(const char *contents, int32_t *Elements, U_PMF_POINTF **Points){ - if(!contents || !Elements || !Points){ return(0); } +int U_PMF_BOUNDARYPOINTDATA_get(const char *contents, int32_t *Elements, U_PMF_POINTF **Points, const char *blimit){ + if(!contents || !Elements || !Points || !blimit){ return(0); } + if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_BOUNDARYPOINTDATA), blimit))return(0); U_PMF_SERIAL_get(&contents, Elements, 4, 1, U_LE); + if(IS_MEM_UNSAFE(contents, *Elements * 2, blimit))return(0); if(!U_PMF_SERIAL_array_copy_get(&contents, (void **)Points, 4, *Elements * 2, U_LE, 1)){ return(0); } return(1); } @@ -5701,8 +5781,9 @@ int U_PMF_BOUNDARYPOINTDATA_get(const char *contents, int32_t *Elements, U_PMF_P EMF+ manual 2.2.2.8, Microsoft name: EmfPlusCharacterRange Object */ -int U_PMF_CHARACTERRANGE_get(const char *contents, int32_t *First, int32_t *Length){ - if(!contents || !First || !Length){ return(0); } +int U_PMF_CHARACTERRANGE_get(const char *contents, int32_t *First, int32_t *Length, const char *blimit){ + if(!contents || !First || !Length || !blimit){ return(0); } + if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_CHARACTERRANGE), blimit))return(0); U_PMF_SERIAL_get(&contents, First, 4, 1, U_LE); U_PMF_SERIAL_get(&contents, Length, 4, 1, U_LE); return(1); @@ -5717,9 +5798,11 @@ int U_PMF_CHARACTERRANGE_get(const char *contents, int32_t *First, int32_t *Leng EMF+ manual 2.2.2.9, Microsoft name: EmfPlusCompoundLineData Object */ -int U_PMF_COMPOUNDLINEDATA_get(const char *contents, int32_t *Elements, U_FLOAT **Widths){ - if(!contents || !Elements || !Widths){ return(0); } +int U_PMF_COMPOUNDLINEDATA_get(const char *contents, int32_t *Elements, U_FLOAT **Widths, const char *blimit){ + if(!contents || !Elements || !Widths || !blimit){ return(0); } + if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_COMPOUNDLINEDATA), blimit))return(0); U_PMF_SERIAL_get(&contents, Elements, 4, 1, U_LE); + if(IS_MEM_UNSAFE(contents, *Elements * sizeof(U_FLOAT), blimit))return(0); *Widths = (U_FLOAT *)malloc(*Elements * sizeof(U_FLOAT)); if(!*Widths){ return(0); } U_PMF_SERIAL_get(&contents, *Widths, 4, *Elements, U_LE); @@ -5736,9 +5819,12 @@ int U_PMF_COMPOUNDLINEDATA_get(const char *contents, int32_t *Elements, U_FLOAT This function does not do anything useful, but it is included so that all objects have a corresponding _get(). + + Caller must check Data for possible memory access violations. */ -int U_PMF_COMPRESSEDIMAGE_get(const char *contents, const char **Data){ - if(!contents || !Data){ return(0); } +int U_PMF_COMPRESSEDIMAGE_get(const char *contents, const char **Data, const char *blimit){ + if(!contents || !Data || !blimit){ return(0); } + if(contents >= blimit)return(0); U_PMF_PTRSAV_SHIFT(Data, &contents, 0); return(1); } @@ -5751,9 +5837,12 @@ int U_PMF_COMPRESSEDIMAGE_get(const char *contents, const char **Data){ \param Data Description of linecap EMF+ manual 2.2.2.11, Microsoft name: EmfPlusCustomEndCapData Object + + Caller must check Data for possible memory access violations. */ -int U_PMF_CUSTOMENDCAPDATA_get(const char *contents, int32_t *Size, const char **Data){ - if(!contents || !Size || !Data){ return(0); } +int U_PMF_CUSTOMENDCAPDATA_get(const char *contents, int32_t *Size, const char **Data, const char *blimit){ + if(!contents || !Size || !Data || !blimit){ return(0); } + if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_CUSTOMENDCAPDATA), blimit))return(0); U_PMF_SERIAL_get(&contents, Size, 4, 1, U_LE); U_PMF_PTRSAV_SHIFT(Data, &contents, 0); return(1); @@ -5767,8 +5856,9 @@ int U_PMF_CUSTOMENDCAPDATA_get(const char *contents, int32_t *Size, const char * EMF+ manual 2.2.2.12, Microsoft name: EmfPlusCustomLineCapArrowData Object */ -int U_PMF_CUSTOMLINECAPARROWDATA_get(const char *contents, U_PMF_CUSTOMLINECAPARROWDATA *Ccad){ - if(!contents || !Ccad){ return(0); } +int U_PMF_CUSTOMLINECAPARROWDATA_get(const char *contents, U_PMF_CUSTOMLINECAPARROWDATA *Ccad, const char *blimit){ + if(!contents || !Ccad || !blimit){ return(0); } + if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_CUSTOMLINECAPARROWDATA), blimit))return(0); U_PMF_SERIAL_get(&contents, Ccad, 4, 13, U_LE); return(1); } @@ -5781,9 +5871,12 @@ int U_PMF_CUSTOMLINECAPARROWDATA_get(const char *contents, U_PMF_CUSTOMLINECAPAR \param Data variable part of U_PMF_CUSTOMLINECAPDATA EMF+ manual 2.2.2.13, Microsoft name: EmfPlusCustomLineCapData Object + + Caller must check Data for possible memory access violations. */ -int U_PMF_CUSTOMLINECAPDATA_get(const char *contents, U_PMF_CUSTOMLINECAPDATA *Clcd, const char **Data){ - if(!contents || !Clcd){ return(0); } +int U_PMF_CUSTOMLINECAPDATA_get(const char *contents, U_PMF_CUSTOMLINECAPDATA *Clcd, const char **Data, const char *blimit){ + if(!contents || !Clcd || !blimit){ return(0); } + if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_CUSTOMLINECAPDATA), blimit))return(0); U_PMF_SERIAL_get(&contents, Clcd, 4, 12, U_LE); U_PMF_PTRSAV_SHIFT(Data, &contents, 0); return(1); @@ -5798,14 +5891,19 @@ int U_PMF_CUSTOMLINECAPDATA_get(const char *contents, U_PMF_CUSTOMLINECAPDATA *C \param LineData Path to stroke (optional) EMF+ manual 2.2.2.14, Microsoft name: EmfPlusCustomLineCapOptionalData Object + + Caller must check LineData for possible memory access violations. */ -int U_PMF_CUSTOMLINECAPOPTIONALDATA_get(const char *contents, uint32_t Flags, const char **FillData, const char **LineData){ +int U_PMF_CUSTOMLINECAPOPTIONALDATA_get(const char *contents, uint32_t Flags, const char **FillData, const char **LineData, const char *blimit){ uint32_t length; int status = 1; - if(!contents){ return(0); } + if(!contents || !*FillData || !*LineData || !blimit){ return(0); } + /* this structure is entirely optional */ if(Flags & U_CLCD_FillPath){ if(!FillData){ return(0); } + if(IS_MEM_UNSAFE(contents, 4, blimit))return(0); U_PMF_SERIAL_get(&contents, &length, 4, 1, U_LE); + if(IS_MEM_UNSAFE(contents, length, blimit))return(0); contents -= 4; /* undo the unneeded shift from preceding */ U_PMF_PTRSAV_SHIFT(FillData, &contents, 4 + length); status += 2; @@ -5814,6 +5912,10 @@ int U_PMF_CUSTOMLINECAPOPTIONALDATA_get(const char *contents, uint32_t Flags, co if(Flags & U_CLCD_LinePath){ if(!LineData){ return(0); } + if(IS_MEM_UNSAFE(contents, 4, blimit))return(0); + U_PMF_SERIAL_get(&contents, &length, 4, 1, U_LE); + if(IS_MEM_UNSAFE(contents, length, blimit))return(0); + contents -= 4; /* undo the unneeded shift from preceding */ U_PMF_PTRSAV_SHIFT(LineData, &contents, 0); status += 4; } @@ -5829,9 +5931,12 @@ int U_PMF_CUSTOMLINECAPOPTIONALDATA_get(const char *contents, uint32_t Flags, co \param Data Description of linecap EMF+ manual 2.2.2.15, Microsoft name: EmfPlusCustomStartCapData Object + + Caller must check Data for possible memory access violations. */ -int U_PMF_CUSTOMSTARTCAPDATA_get(const char *contents, int32_t *Size, const char **Data){ - if(!contents || !Size || !Data){ return(0); } +int U_PMF_CUSTOMSTARTCAPDATA_get(const char *contents, int32_t *Size, const char **Data, const char *blimit){ + if(!contents || !Size || !Data || !blimit){ return(0); } + if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_CUSTOMSTARTCAPDATA), blimit))return(0); U_PMF_SERIAL_get(&contents, Size, 4, 1, U_LE); U_PMF_PTRSAV_SHIFT(Data, &contents, 0); return(1); @@ -5846,9 +5951,11 @@ int U_PMF_CUSTOMSTARTCAPDATA_get(const char *contents, int32_t *Size, const char EMF+ manual 2.2.2.16, Microsoft name: EmfPlusDashedLineData Object */ -int U_PMF_DASHEDLINEDATA_get(const char *contents, int32_t *Elements, U_FLOAT **Lengths){ - if(!contents || !Elements || !Lengths){ return(0); } +int U_PMF_DASHEDLINEDATA_get(const char *contents, int32_t *Elements, U_FLOAT **Lengths, const char *blimit){ + if(!contents || !Elements || !Lengths || !blimit){ return(0); } + if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_DASHEDLINEDATA), blimit))return(0); U_PMF_SERIAL_get(&contents, Elements, 4, 1, U_LE); + if(IS_MEM_UNSAFE(contents, *Elements * sizeof(U_FLOAT), blimit))return(0); *Lengths = (U_FLOAT *)malloc(*Elements * sizeof(U_FLOAT)); if(!*Lengths){ return(0); } U_PMF_SERIAL_get(&contents, *Lengths, 4, *Elements, U_LE); @@ -5863,9 +5970,12 @@ int U_PMF_DASHEDLINEDATA_get(const char *contents, int32_t *Elements, U_FLOAT ** \param Data Path specification EMF+ manual 2.2.2.17, Microsoft name: EmfPlusFillPath Object + + Caller must check Data for possible memory access violations. */ -int U_PMF_FILLPATHOBJ_get(const char *contents, int32_t *Size, const char **Data){ - if(!contents || !Size || !Data){ return(0); } +int U_PMF_FILLPATHOBJ_get(const char *contents, int32_t *Size, const char **Data, const char *blimit){ + if(!contents || !Size || !Data || !blimit){ return(0); } + if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_FILLPATHO), blimit))return(0); U_PMF_SERIAL_get(&contents, Size, 4, 1, U_LE); U_PMF_PTRSAV_SHIFT(Data, &contents, 0); return(1); @@ -5881,8 +5991,9 @@ int U_PMF_FILLPATHOBJ_get(const char *contents, int32_t *Size, const char **Data EMF+ manual 2.2.2.18, Microsoft name: EmfPlusFocusScaleData Object */ -int U_PMF_FOCUSSCALEDATA_get(const char *contents, uint32_t *Count, U_FLOAT *ScaleX, U_FLOAT *ScaleY){ - if(!contents || !Count || !ScaleX || !ScaleY){ return(0); } +int U_PMF_FOCUSSCALEDATA_get(const char *contents, uint32_t *Count, U_FLOAT *ScaleX, U_FLOAT *ScaleY, const char *blimit){ + if(!contents || !Count || !ScaleX || !ScaleY || !blimit){ return(0); } + if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_FOCUSSCALEDATA), blimit))return(0); U_PMF_SERIAL_get(&contents, Count, 4, 1, U_LE); if(*Count != 2){ return(0); } U_PMF_SERIAL_get(&contents, ScaleX, 4, 1, U_LE); @@ -5899,9 +6010,10 @@ int U_PMF_FOCUSSCALEDATA_get(const char *contents, uint32_t *Count, U_FLOAT *Sca EMF+ manual 2.2.2.19, Microsoft name: EmfPlusGraphicsVersion Object */ -int U_PMF_GRAPHICSVERSION_get(const char *contents, int *Signature, int *GrfVersion){ - if(!contents || !Signature || !GrfVersion){ return(0); } +int U_PMF_GRAPHICSVERSION_get(const char *contents, int *Signature, int *GrfVersion, const char *blimit){ uint32_t tmp; + if(!contents || !Signature || !GrfVersion || !blimit){ return(0); } + if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_GRAPHICSVERSION), blimit))return(0); memcpy(&tmp, contents, 4); *Signature = tmp >> 12; *GrfVersion = tmp & U_GFVR_MASKLO; @@ -5918,8 +6030,9 @@ int U_PMF_GRAPHICSVERSION_get(const char *contents, int *Signature, int *GrfVers EMF+ manual 2.2.2.20, Microsoft name: EmfPlusHatchBrushData Object */ -int U_PMF_HATCHBRUSHDATA_get(const char *contents, uint32_t *Style, U_PMF_ARGB *Foreground, U_PMF_ARGB *Background){ - if(!contents || !Style || !Foreground || !Background){ return(0); } +int U_PMF_HATCHBRUSHDATA_get(const char *contents, uint32_t *Style, U_PMF_ARGB *Foreground, U_PMF_ARGB *Background, const char *blimit){ + if(!contents || !Style || !Foreground || !Background || !blimit){ return(0); } + if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_HATCHBRUSHDATA), blimit))return(0); U_PMF_SERIAL_get(&contents, Style, 4, 1, U_LE); U_PMF_SERIAL_get(&contents, Foreground, 4, 1, U_XE); U_PMF_SERIAL_get(&contents, Background, 4, 1, U_XE); @@ -5931,12 +6044,14 @@ int U_PMF_HATCHBRUSHDATA_get(const char *contents, uint32_t *Style, U_PMF_ARGB * \return 1 on success, 0 on error \param contents Record from which to extract data \param Value 7 bit signed integer (stored in an integer) + \param blimit one byte past the end of data EMF+ manual 2.2.2.21, Microsoft name: EmfPlusInteger7 Object */ -int U_PMF_INTEGER7_get(const char **contents, U_FLOAT *Value){ - if(!contents || !*contents || !Value){ return(0); } +int U_PMF_INTEGER7_get(const char **contents, U_FLOAT *Value, const char *blimit){ uint8_t tmp; + if(!contents || !*contents || !Value || !blimit){ return(0); } + if(IS_MEM_UNSAFE(*contents, 1, blimit))return(0); /* past end of buffer */ if(**contents & U_TEST_INT7)return(0); /* this bit must be 0 in this object type */ U_PMF_SERIAL_get(contents, &tmp, 1, 1, U_XE); if(tmp & U_SIGN_INT7){ @@ -5954,12 +6069,14 @@ int U_PMF_INTEGER7_get(const char **contents, U_FLOAT *Value){ \return 1 on success, 0 on error \param contents Record from which to extract data \param Value 15 bit signed integer (stored in an integer) + \param blimit one byte past the end of data EMF+ manual 2.2.2.22, Microsoft name: EmfPlusInteger15 Object */ -int U_PMF_INTEGER15_get(const char **contents, U_FLOAT *Value){ - if(!contents || !*contents || !Value){ return(0); } +int U_PMF_INTEGER15_get(const char **contents, U_FLOAT *Value, const char *blimit){ + if(!contents || !*contents || !Value || !blimit){ return(0); } uint16_t tmp; + if(IS_MEM_UNSAFE(*contents, 2, blimit))return(0); /* past end of buffer */ if(!(**contents & U_TEST_INT7))return(0); /* this bit must be 1 in this object type */ U_PMF_SERIAL_get(contents, &tmp, 2, 1, U_BE); tmp &= U_MASK_INT15; /* drop the 7/15 flag from the most significant bit */ @@ -6000,11 +6117,15 @@ int U_PMF_LANGUAGEIDENTIFIER_get(U_PMF_LANGUAGEIDENTIFIER LId, int *SubLId, int \param contents Record from which to extract data \param Lgbd U_PMF_LINEARGRADIENTBRUSHDATA structure, with no variable part \param Data variable part of U_PMF_LINEARGRADIENTBRUSHDATA + \param blimit one byte past the end of data EMF+ manual 2.2.2.24, Microsoft name: EmfPlusLinearGradientBrushData Object + + Caller must check Data for possible memory access violations. */ -int U_PMF_LINEARGRADIENTBRUSHDATA_get(const char *contents, U_PMF_LINEARGRADIENTBRUSHDATA *Lgbd, const char **Data){ - if(!contents || !Lgbd || !Data){ return(0); } +int U_PMF_LINEARGRADIENTBRUSHDATA_get(const char *contents, U_PMF_LINEARGRADIENTBRUSHDATA *Lgbd, const char **Data, const char *blimit){ + if(!contents || !Lgbd || !Data || !blimit){ return(0); } + if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_LINEARGRADIENTBRUSHDATA), blimit))return(0); U_PMF_SERIAL_get(&contents, Lgbd, 4, 6, U_LE); /* Flags, WrapMode, RectF*/ U_PMF_SERIAL_get(&contents, &(Lgbd->StartColor), 4, 4, U_XE); /* StartColor, EndColor, Reserved1 & 2 */ U_PMF_PTRSAV_SHIFT(Data, &contents, 0); @@ -6020,27 +6141,42 @@ int U_PMF_LINEARGRADIENTBRUSHDATA_get(const char *contents, U_PMF_LINEARGRADIENT \param Bc U_PMF_BLENDCOLORS object or NULL \param BfH U_PMF_BLENDFACTORS (H) object or NULL \param BfV U_PMF_BLENDFACTORS (V) object or NULL (WARNING, GDI+ defines this field but does not render it. DO NOT USE.) + \param blimit one byte past the end of data EMF+ manual 2.2.2.25, Microsoft name: EmfPlusLinearGradientBrushOptionalData Object + */ int U_PMF_LINEARGRADIENTBRUSHOPTIONALDATA_get(const char *contents, uint32_t Flags, U_PMF_TRANSFORMMATRIX *Tm, - const char **Bc, const char **BfH, const char **BfV){ + const char **Bc, const char **BfH, const char **BfV, const char *blimit){ uint32_t Elements; - if(!contents || !Tm|| !Bc || !BfH || !BfV){ return(0); } + if(!contents || !Tm|| !Bc || !BfH || !BfV || !blimit){ return(0); } + /* all of the fields are optional! */ *Bc = *BfH = *BfV = NULL; - if(Flags & U_BD_Transform)U_PMF_SERIAL_get(&contents, Tm, 4, 6, U_LE); + if(Flags & U_BD_Transform){ + if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_ROTMATRIX), blimit))return(0); + U_PMF_SERIAL_get(&contents, Tm, 4, 6, U_LE); + } if(Flags & U_BD_PresetColors){ + if(IS_MEM_UNSAFE(contents, 4, blimit))return(0); + U_PMF_SERIAL_get(&contents, &Elements, 4, 1, U_LE); /* starts with a 4 byte count*/ + if(IS_MEM_UNSAFE(contents, Elements * ( sizeof(U_FLOAT) + sizeof(U_PMF_ARGB)), blimit))return(0); + contents-=4; /* back up to the front of the count, as it is part of the data field */ U_PMF_PTRSAV_SHIFT(Bc, &contents, 0); } else if(Flags & U_BD_BlendFactorsH){ U_PMF_SERIAL_get(&contents, &Elements, 4, 1, U_LE); /* starts with a 4 byte count*/ + if(IS_MEM_UNSAFE(contents, Elements * 2 * sizeof(U_FLOAT), blimit))return(0); contents-=4; /* back up to the front of the count, as it is part of the data field */ - U_PMF_PTRSAV_SHIFT(BfH, &contents, 4 + 8*Elements); /* 4 byte count + 2 * 4bytes * Elements */ + U_PMF_PTRSAV_SHIFT(BfH, &contents, 4 + (Elements * 2 * sizeof(U_FLOAT))); /* 4 byte count + 2 * 4bytes * Elements */ if(Flags & U_BD_BlendFactorsV){ + if(IS_MEM_UNSAFE(contents, Elements * 2 * sizeof(U_FLOAT), blimit))return(0); U_PMF_PTRSAV_SHIFT(BfV, &contents, 0); } } else if(Flags & U_BD_BlendFactorsV){ + U_PMF_SERIAL_get(&contents, &Elements, 4, 1, U_LE); /* starts with a 4 byte count*/ + if(IS_MEM_UNSAFE(contents, Elements * 2 * sizeof(U_FLOAT), blimit))return(0); + contents-=4; /* back up to the front of the count, as it is part of the data field */ U_PMF_PTRSAV_SHIFT(BfV, &contents, 0); } return(1); @@ -6052,11 +6188,15 @@ int U_PMF_LINEARGRADIENTBRUSHOPTIONALDATA_get(const char *contents, uint32_t Fla \param contents Record from which to extract data \param Size Bytes in Data \param Data Outline path + \param blimit one byte past the end of data EMF+ manual 2.2.2.26, Microsoft name: EmfPlusLinePath Object + + Caller must check Data for possible memory access violations. */ -int U_PMF_LINEPATH_get(const char *contents, int32_t *Size, const char **Data){ - if(!contents || !Size || !Data){ return(0); } +int U_PMF_LINEPATH_get(const char *contents, int32_t *Size, const char **Data, const char *blimit){ + if(!contents || !Size || !Data || !blimit){ return(0); } + if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_LINEPATH), blimit))return(0); U_PMF_SERIAL_get(&contents, Size, 4, 1, U_LE); U_PMF_PTRSAV_SHIFT(Data, &contents, 0); return(1); @@ -6069,11 +6209,15 @@ int U_PMF_LINEPATH_get(const char *contents, int32_t *Size, const char **Data){ \param Type \param Size Bytes in Data \param Data Various types of data, like an EMF metafile, WMF metafile, another EMF+ metafile + \param blimit one byte past the end of data EMF+ manual 2.2.2.27, Microsoft name: EmfPlusMetafile Object + + Caller must check Data for possible memory access violations. */ -int U_PMF_METAFILE_get(const char *contents, uint32_t *Type, uint32_t *Size, const char **Data){ - if(!contents || !Type || !Size || !Data){ return(0); } +int U_PMF_METAFILE_get(const char *contents, uint32_t *Type, uint32_t *Size, const char **Data, const char *blimit){ + if(!contents || !Type || !Size || !Data || !blimit){ return(0); } + if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_METAFILE), blimit))return(0); U_PMF_SERIAL_get(&contents, &Type, 4, 1, U_LE); U_PMF_SERIAL_get(&contents, &Size, 4, 1, U_LE); U_PMF_PTRSAV_SHIFT(Data, &contents, 0); @@ -6087,13 +6231,16 @@ int U_PMF_METAFILE_get(const char *contents, uint32_t *Type, uint32_t *Size, con \param Flags PaletteStyle flags \param Elements Members in the array \param Colors Palette data (array of colors) + \param blimit one byte past the end of data EMF+ manual 2.2.2.28, Microsoft name: EmfPlusPalette Object */ -int U_PMF_PALETTE_get(const char *contents, uint32_t *Flags, uint32_t *Elements, const char **Colors){ - if(!contents || !Flags || !Elements || !Colors){ return(0); } +int U_PMF_PALETTE_get(const char *contents, uint32_t *Flags, uint32_t *Elements, const char **Colors, const char *blimit){ + if(!contents || !Flags || !Elements || !Colors || !blimit){ return(0); } + if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_PALETTE), blimit))return(0); U_PMF_SERIAL_get(&contents, &Flags, 4, 1, U_LE); U_PMF_SERIAL_get(&contents, &Elements, 4, 1, U_LE); + if(IS_MEM_UNSAFE(contents, *Elements*sizeof(U_RGBQUAD), blimit))return(0); U_PMF_PTRSAV_SHIFT(Colors, &contents, 0); return(1); @@ -6107,21 +6254,32 @@ int U_PMF_PALETTE_get(const char *contents, uint32_t *Flags, uint32_t *Elements, \param Gradient variable part of U_PMF_LINEARGRADIENTBRUSHDATA, Color Gradient with Elements members \param Boundary variable part of U_PMF_LINEARGRADIENTBRUSHDATA, U_PMF_BOUNDARYPATHDATA object if BrushDataPath bit set in Flag, else U_PMF_BOUNDARYPOINTDATA object \param Data variable part of U_PMF_LINEARGRADIENTBRUSHDATA, exact composition depends on Flags + \param blimit one byte past the end of data EMF+ manual 2.2.2.29, Microsoft name: EmfPlusPathGradientBrushData Object + + Caller must check Data for possible memory access violations. + */ -int U_PMF_PATHGRADIENTBRUSHDATA_get(const char *contents, U_PMF_PATHGRADIENTBRUSHDATA *Pgbd, const char **Gradient, const char **Boundary, const char **Data){ - if(!contents || !Pgbd || !Gradient || !Boundary || !Data){ return(0); } +int U_PMF_PATHGRADIENTBRUSHDATA_get(const char *contents, U_PMF_PATHGRADIENTBRUSHDATA *Pgbd, const char **Gradient, + const char **Boundary, const char **Data, const char *blimit){ + if(!contents || !Pgbd || !Gradient || !Boundary || !Data || !blimit){ return(0); } + if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_PATHGRADIENTBRUSHDATA), blimit))return(0); uint32_t Size; U_PMF_SERIAL_get(&contents, Pgbd, 4, 2, U_LE); /* Flags and WrapMode*/ U_PMF_SERIAL_get(&contents, &(Pgbd->CenterColor), 4, 1, U_XE); U_PMF_SERIAL_get(&contents, &(Pgbd->Center), 4, 3, U_LE); /* Center and Elements */ + if(IS_MEM_UNSAFE(contents, Pgbd->Elements * sizeof(U_PMF_ARGB), blimit))return(0); U_PMF_PTRSAV_SHIFT(Gradient, &contents, Pgbd->Elements * sizeof(U_PMF_ARGB)); U_PMF_PTRSAV_SHIFT(Boundary, &contents, 0); U_PMF_SERIAL_get(&contents, &Size, 4, 1, U_LE); /* The first 4 bytes of the Boundary are always a size */ if(Pgbd->Flags & U_BD_Path){ contents += Size; } // U_PMF_BOUNDARYPATHDATA else { contents += Size*2*sizeof(U_FLOAT); } // U_PMF_BOUNDARYPOINTDATA - U_PMF_PTRSAV_SHIFT(Data, &contents, 0); + if(Pgbd->Flags & (U_BD_Transform |U_BD_PresetColors | U_BD_BlendFactorsH| U_BD_FocusScales)){ // optional data present + if(contents >= blimit)return(0); // the size is variable but this must still hold + U_PMF_PTRSAV_SHIFT(Data, &contents, 0); + } + else { *Data = NULL; } // no optional data present return(1); } @@ -6133,15 +6291,34 @@ int U_PMF_PATHGRADIENTBRUSHDATA_get(const char *contents, U_PMF_PATHGRADIENTBRUS \param Matrix Transformation matrix \param Pattern Blend Pattern \param Data Focus scales for the brush + \param blimit one byte past the end of data EMF+ manual 2.2.2.30, Microsoft name: EmfPlusPathGradientBrushOptionalData Object */ -int U_PMF_PATHGRADIENTBRUSHOPTIONALDATA_get(const char *contents, uint32_t Flags, U_PMF_TRANSFORMMATRIX *Matrix, const char **Pattern, const char **Data){ - if(!contents || !Flags || !Matrix || !Pattern || !Data){ return(0); } - U_PMF_SERIAL_get(&contents, &Flags, 4, 1, U_LE); - U_PMF_SERIAL_get(&contents, Matrix, 4, 6, U_LE); - U_PMF_PTRSAV_COND(Data, contents, (Flags & (U_BD_PresetColors | U_BD_BlendFactorsH))); - U_PMF_PTRSAV_COND(Data, contents, (Flags & U_BD_FocusScales)); +int U_PMF_PATHGRADIENTBRUSHOPTIONALDATA_get(const char *contents, uint32_t Flags, U_PMF_TRANSFORMMATRIX *Matrix, + const char **Pattern, const char **Data, const char *blimit){ + int varsize; + if(!contents || !Flags || !Matrix || !Pattern || !Data || !blimit){ return(0); } + /* this structure is entirely optional */ + if(Flags & U_BD_Transform){ + if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_TRANSFORMMATRIX), blimit))return(0); + U_PMF_SERIAL_get(&contents, Matrix, 4, 6, U_LE); + } + if(Flags & (U_BD_PresetColors | U_BD_BlendFactorsH)){ + if(IS_MEM_UNSAFE(contents, 4, blimit))return(0); + uint32_t Elements; + U_PMF_SERIAL_get(&contents, &Elements, 4, 1, U_LE); + contents -= 4; + varsize=(Elements * 4 * (Flags & U_BD_BlendFactorsH ? 2 :1)); + if(IS_MEM_UNSAFE(contents, varsize, blimit))return(0); + U_PMF_PTRSAV_SHIFT(Pattern, &contents, varsize); + } + else { *Pattern=NULL; } + if(Flags & U_BD_FocusScales){ + if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_FOCUSSCALEDATA), blimit))return(0); + U_PMF_PTRSAV_SHIFT(Data, &contents, sizeof(U_PMF_FOCUSSCALEDATA)); + } + else { *Data=NULL; } return(1); } @@ -6151,14 +6328,16 @@ int U_PMF_PATHGRADIENTBRUSHOPTIONALDATA_get(const char *contents, uint32_t Flags \param contents Record from which to extract data \param Flags PathPointType flags \param Type PathPointType enumeration + \param blimit one byte past the end of data EMF+ manual 2.2.2.31, Microsoft name: EmfPlusPathPointType Object Note: order of 4bit fields appears to be shown in the LE column, not as documented in the BE column. */ -int U_PMF_PATHPOINTTYPE_get(const char *contents, int *Flags, int *Type){ - if(!contents || !Flags || !Type){ return(0); } +int U_PMF_PATHPOINTTYPE_get(const char *contents, int *Flags, int *Type, const char *blimit){ + if(!contents || !Flags || !Type || !blimit){ return(0); } + if(IS_MEM_UNSAFE(contents, 1, blimit))return(0); uint8_t tmp; memcpy(&tmp, contents, 1); *Flags =(tmp & U_PTP_MASK) >> U_PTP_SHIFT; @@ -6172,13 +6351,15 @@ int U_PMF_PATHPOINTTYPE_get(const char *contents, int *Flags, int *Type){ \param contents Record from which to extract data \param Bezier Set: Bezier curve, Clear: straight line \param RL Run Length - \param Ppt PathPointType enumeration + \param Ppt PathPointType enumeration + \param blimit one byte past the end of data EMF+ manual 2.2.2.32, Microsoft name: EmfPlusPathPointTypeRLE Object */ -int U_PMF_PATHPOINTTYPERLE_get(const char *contents, int *Bezier, int *RL, int *Ppt){ - if(!contents || !Bezier || !RL || !Ppt){ return(0); } +int U_PMF_PATHPOINTTYPERLE_get(const char *contents, int *Bezier, int *RL, int *Ppt, const char *blimit){ + if(!contents || !Bezier || !RL || !Ppt || !blimit){ return(0); } uint16_t tmp; + if(IS_MEM_UNSAFE(contents, 2, blimit))return(0); U_PMF_SERIAL_get(&contents, &tmp, 2, 1, U_LE); *Bezier = tmp & U_PPF_BZ; *RL = (tmp >> U_FF_SHFT_RL) & U_FF_MASK_RL; @@ -6194,14 +6375,17 @@ int U_PMF_PATHPOINTTYPERLE_get(const char *contents, int *Bezier, int *RL, int * \param Unit UnitType enumeration \param Width Width in units set by Unit \param Data Optional pen data, exact composition depends on Flags + \param blimit one byte past the end of data EMF+ manual 2.2.2.33, Microsoft name: EmfPlusPenData Object */ -int U_PMF_PENDATA_get(const char *contents, uint32_t *Flags, uint32_t *Unit, U_FLOAT *Width, const char **Data){ - if(!contents || !Flags || !Unit || !Width || !Data){ return(0); } +int U_PMF_PENDATA_get(const char *contents, uint32_t *Flags, uint32_t *Unit, U_FLOAT *Width, const char **Data, const char *blimit){ + if(!contents || !Flags || !Unit || !Width || !Data || !blimit){ return(0); } + if(IS_MEM_UNSAFE(contents, 3*4, blimit))return(0); U_PMF_SERIAL_get(&contents, Flags, 4, 1, U_LE); U_PMF_SERIAL_get(&contents, Unit, 4, 1, U_LE); U_PMF_SERIAL_get(&contents, Width, 4, 1, U_LE); + if(contents >= blimit)return(0); // variable data will extend farther, but this much at least must be true U_PMF_PTRSAV_SHIFT(Data, &contents, 0); return(1); } @@ -6224,6 +6408,7 @@ int U_PMF_PENDATA_get(const char *contents, uint32_t *Flags, uint32_t *Unit, U_F \param CmpndLineData Compount Line (parallel lines drawn instead of one) \param CSCapData Custom start cap \param CECapData Custom end cap + \param blimit one byte past the end of data EMF+ manual 2.2.2.34, Microsoft name: EmfPlusPenOptionalData Object @@ -6245,26 +6430,56 @@ int U_PMF_PENOPTIONALDATA_get( int32_t *Alignment, const char **CmpndLineData, const char **CSCapData, - const char **CECapData){ + const char **CECapData, + const char *blimit){ if(!contents || !Flags || !Matrix || !StartCap || !EndCap || !Join || !MiterLimit || !Style || !DLCap || !DLOffset || !DLData || !Alignment || !CmpndLineData || - !CSCapData || !CECapData){ return(0); } - - if(Flags & U_PD_Transform){ U_PMF_SERIAL_get(&contents, Matrix, 4, 6, U_LE); } - if(Flags & U_PD_StartCap){ U_PMF_SERIAL_get(&contents, StartCap, 4, 1, U_LE); } - if(Flags & U_PD_EndCap){ U_PMF_SERIAL_get(&contents, EndCap, 4, 1, U_LE); } - if(Flags & U_PD_Join){ U_PMF_SERIAL_get(&contents, Join, 4, 1, U_LE); } - if(Flags & U_PD_MiterLimit){ U_PMF_SERIAL_get(&contents, MiterLimit, 4, 1, U_LE); } - if(Flags & U_PD_LineStyle){ U_PMF_SERIAL_get(&contents, Style, 4, 1, U_LE); } - if(Flags & U_PD_DLCap){ U_PMF_SERIAL_get(&contents, DLCap, 4, 1, U_LE); } - if(Flags & U_PD_DLOffset){ U_PMF_SERIAL_get(&contents, DLOffset, 4, 1, U_LE); } - if(Flags & U_PD_DLData){ U_PMF_PTRSAV_SHIFT( DLData, &contents, U_PMF_LEN_FLOATDATA(contents)); } - if(Flags & U_PD_NonCenter){ U_PMF_SERIAL_get(&contents, Alignment, 4, 1, U_LE); } - if(Flags & U_PD_CLData){ U_PMF_PTRSAV_SHIFT( CmpndLineData, &contents, U_PMF_LEN_FLOATDATA(contents)); } - if(Flags & U_PD_CustomStartCap){ U_PMF_PTRSAV_SHIFT( CSCapData, &contents, U_PMF_LEN_BYTEDATA(contents)); } - if(Flags & U_PD_CustomEndCap){ U_PMF_PTRSAV_SHIFT( CECapData, &contents, U_PMF_LEN_BYTEDATA(contents)); } + !CSCapData || !CECapData || !blimit){ return(0); } + + if(Flags & U_PD_Transform){ if(IS_MEM_UNSAFE(contents, 4*6, blimit))return(0); + U_PMF_SERIAL_get(&contents, Matrix, 4, 6, U_LE); + } + if(Flags & U_PD_StartCap){ if(IS_MEM_UNSAFE(contents, 4, blimit))return(0); + U_PMF_SERIAL_get(&contents, StartCap, 4, 1, U_LE); + } + if(Flags & U_PD_EndCap){ if(IS_MEM_UNSAFE(contents, 4, blimit))return(0); + U_PMF_SERIAL_get(&contents, EndCap, 4, 1, U_LE); + } + if(Flags & U_PD_Join){ if(IS_MEM_UNSAFE(contents, 4, blimit))return(0); + U_PMF_SERIAL_get(&contents, Join, 4, 1, U_LE); + } + if(Flags & U_PD_MiterLimit){ if(IS_MEM_UNSAFE(contents, 4, blimit))return(0); + U_PMF_SERIAL_get(&contents, MiterLimit, 4, 1, U_LE); + } + if(Flags & U_PD_LineStyle){ if(IS_MEM_UNSAFE(contents, 4, blimit))return(0); + U_PMF_SERIAL_get(&contents, Style, 4, 1, U_LE); + } + if(Flags & U_PD_DLCap){ if(IS_MEM_UNSAFE(contents, 4, blimit))return(0); + U_PMF_SERIAL_get(&contents, DLCap, 4, 1, U_LE); + } + if(Flags & U_PD_DLOffset){ if(IS_MEM_UNSAFE(contents, 4, blimit))return(0); + U_PMF_SERIAL_get(&contents, DLOffset, 4, 1, U_LE); + } + if(Flags & U_PD_DLData){ if(IS_MEM_UNSAFE(contents, 4, blimit))return(0); + if(IS_MEM_UNSAFE(contents, U_PMF_LEN_FLOATDATA(contents), blimit))return(0); + U_PMF_PTRSAV_SHIFT( DLData, &contents, U_PMF_LEN_FLOATDATA(contents)); + } + if(Flags & U_PD_NonCenter){ if(IS_MEM_UNSAFE(contents, 4, blimit))return(0); + U_PMF_SERIAL_get(&contents, Alignment, 4, 1, U_LE); } + if(Flags & U_PD_CLData){ if(IS_MEM_UNSAFE(contents, 4, blimit))return(0); + if(IS_MEM_UNSAFE(contents, U_PMF_LEN_FLOATDATA(contents), blimit))return(0); + U_PMF_PTRSAV_SHIFT( CmpndLineData, &contents, U_PMF_LEN_FLOATDATA(contents)); + } + if(Flags & U_PD_CustomStartCap){ if(IS_MEM_UNSAFE(contents, 4, blimit))return(0); + if(IS_MEM_UNSAFE(contents, U_PMF_LEN_BYTEDATA(contents), blimit))return(0); + U_PMF_PTRSAV_SHIFT( CSCapData, &contents, U_PMF_LEN_BYTEDATA(contents)); + } + if(Flags & U_PD_CustomEndCap){ if(IS_MEM_UNSAFE(contents, 4, blimit))return(0); + if(IS_MEM_UNSAFE(contents, U_PMF_LEN_BYTEDATA(contents), blimit))return(0); + U_PMF_PTRSAV_SHIFT( CECapData, &contents, U_PMF_LEN_BYTEDATA(contents)); + } return(1); } @@ -6274,12 +6489,14 @@ int U_PMF_PENOPTIONALDATA_get( \param contents Record from which to extract data. On return position is offset by sizeof(U_PMF_POINT). \param X X coordinate \param Y Y coordinate + \param blimit one byte past the end of data EMF+ manual 2.2.2.35, Microsoft name: EmfPlusPoint Object */ -int U_PMF_POINT_get(const char **contents, U_FLOAT *X, U_FLOAT *Y){ - if(!contents || !X || !Y){ return(0); } +int U_PMF_POINT_get(const char **contents, U_FLOAT *X, U_FLOAT *Y, const char *blimit){ + if(!contents || !X || !Y || !blimit){ return(0); } int16_t tmp; + if(IS_MEM_UNSAFE(*contents, 2*2, blimit))return(0); U_PMF_SERIAL_get(contents, &tmp, 2, 1, U_LE); *X = tmp; U_PMF_SERIAL_get(contents, &tmp, 2, 1, U_LE); *Y = tmp; return(1); @@ -6291,11 +6508,13 @@ int U_PMF_POINT_get(const char **contents, U_FLOAT *X, U_FLOAT *Y){ \param contents Record from which to extract data. On return position is offset by sizeof(U_PMF_POINTF). \param X X coordinate \param Y Y coordinate + \param blimit one byte past the end of data EMF+ manual 2.2.2.36, Microsoft name: EmfPlusPointF Object */ -int U_PMF_POINTF_get(const char **contents, U_FLOAT *X, U_FLOAT *Y){ - if(!contents || !X || !Y){ return(0); } +int U_PMF_POINTF_get(const char **contents, U_FLOAT *X, U_FLOAT *Y, const char *blimit){ + if(!contents || !X || !Y || !blimit){ return(0); } + if(IS_MEM_UNSAFE(*contents, 4*2, blimit))return(0); U_PMF_SERIAL_get(contents, X, 4, 1, U_LE); U_PMF_SERIAL_get(contents, Y, 4, 1, U_LE); return(1); @@ -6307,19 +6526,20 @@ int U_PMF_POINTF_get(const char **contents, U_FLOAT *X, U_FLOAT *Y){ \param contents Record from which to extract data. On return position is offset by returned size. \param X X coordinate \param Y Y coordinate + \param blimit one byte past the end of data EMF+ manual 2.2.2.37, Microsoft name: EmfPlusPointR Object */ -int U_PMF_POINTR_get(const char **contents, U_FLOAT *X, U_FLOAT *Y){ - if(!contents || !*contents | !X || !Y){ return(0); } +int U_PMF_POINTR_get(const char **contents, U_FLOAT *X, U_FLOAT *Y, const char *blimit){ + if(!contents || !*contents | !X || !Y || !blimit){ return(0); } int size=0; - if( U_PMF_INTEGER7_get( contents, X)){ size +=1; } - else if(U_PMF_INTEGER15_get(contents, X)){ size +=2; } + if( U_PMF_INTEGER7_get( contents, X, blimit)){ size +=1; } + else if(U_PMF_INTEGER15_get(contents, X, blimit)){ size +=2; } else { return(0); } - if( U_PMF_INTEGER7_get( contents, Y)){ size +=1; } - else if(U_PMF_INTEGER15_get(contents, Y)){ size +=2; } + if( U_PMF_INTEGER7_get( contents, Y, blimit)){ size +=1; } + else if(U_PMF_INTEGER15_get(contents, Y, blimit)){ size +=2; } else { return(0); } return(size); @@ -6332,31 +6552,40 @@ int U_PMF_POINTR_get(const char **contents, U_FLOAT *X, U_FLOAT *Y){ \param Flags Record flags (bits U_PPF_C and U_PPF_P are referenced) \param Elements Number of points to retrieve. \param Points Caller must free. Array of U_PMF_POINTF coordinates. + \param blimit one byte past the end of data + + This function should never be called directly by end user code. */ -int U_PMF_VARPOINTS_get(const char **contents, uint16_t Flags, int Elements, U_PMF_POINTF **Points){ +int U_PMF_VARPOINTS_get(const char *contents, uint16_t Flags, int Elements, U_PMF_POINTF **Points, const char *blimit){ int status = 0; - if(!contents || !*contents || !Points || !Elements){ return(status); } + if(!contents || !Points || !Elements || !blimit){ return(status); } U_PMF_POINTF *pts = (U_PMF_POINTF *)malloc(Elements * sizeof(U_PMF_POINTF)); if(!pts){ return(status); } *Points = pts; U_FLOAT XF, YF; U_FLOAT XFS, YFS; - for(XFS = YFS = 0.0; Elements; Elements--, pts++){ - if(Flags & U_PPF_P){ - if(!U_PMF_POINTR_get(contents, &XF, &YF))break; /* this should never happen */ + if(Flags & U_PPF_P){ + for(XFS = YFS = 0.0; Elements; Elements--, pts++){ + if(!U_PMF_POINTR_get(&contents, &XF, &YF, blimit))return(0); /* this should never happen */ XFS += XF; /* position relative to previous point, first point is always 0,0 */ YFS += YF; pts->X = XFS; pts->Y = YFS; } - else if(Flags & U_PPF_C){ - if(!U_PMF_POINT_get(contents, &XF, &XF))break; /* this should never happen */ + } + else if(Flags & U_PPF_C){ + for(XF = YF = 0.0; Elements; Elements--, pts++){ + if(!U_PMF_POINT_get(&contents, &XF, &XF, blimit))break; /* this should never happen */ pts->X = XF; pts->Y = YF; } - else { - (void) U_PMF_POINTF_get(contents, &(pts->X), &(pts->Y)); + } + else { + for(XF = YF = 0.0; Elements; Elements--, pts++){ + (void) U_PMF_POINTF_get(&contents, &XF, &YF, blimit); + pts->X = XF; + pts->Y = YF; } } if(Elements){ /* some error in the preceding */ @@ -6377,11 +6606,13 @@ int U_PMF_VARPOINTS_get(const char **contents, uint16_t Flags, int Elements, U_P \param Y UL Y value \param Width Width \param Height Height + \param blimit one byte past the end of data EMF+ manual 2.2.2.38, Microsoft name: EmfPlusRect Object */ -int U_PMF_RECT_get(const char **contents, int16_t *X, int16_t *Y, int16_t *Width, int16_t *Height){ +int U_PMF_RECT_get(const char **contents, int16_t *X, int16_t *Y, int16_t *Width, int16_t *Height, const char *blimit){ if(!contents || !X || !Y|| !Width || !Height){ return(0); } + if(IS_MEM_UNSAFE(*contents, 2*4, blimit))return(0); U_PMF_SERIAL_get(contents, X, 2, 1, U_LE); U_PMF_SERIAL_get(contents, Y, 2, 1, U_LE); U_PMF_SERIAL_get(contents, Width, 2, 1, U_LE); @@ -6397,11 +6628,13 @@ int U_PMF_RECT_get(const char **contents, int16_t *X, int16_t *Y, int16_t *Width \param Y UL Y value \param Width Width \param Height Height + \param blimit one byte past the end of data EMF+ manual 2.2.2.39, Microsoft name: EmfPlusRectF Object */ -int U_PMF_RECTF_get(const char **contents, U_FLOAT *X, U_FLOAT *Y, U_FLOAT *Width, U_FLOAT *Height){ +int U_PMF_RECTF_get(const char **contents, U_FLOAT *X, U_FLOAT *Y, U_FLOAT *Width, U_FLOAT *Height, const char *blimit){ if(!contents || !X || !Y|| !Width || !Height){ return(0); } + if(IS_MEM_UNSAFE(*contents, 4*4, blimit))return(0); U_PMF_SERIAL_get(contents, X, 4, 1, U_LE); U_PMF_SERIAL_get(contents, Y, 4, 1, U_LE); U_PMF_SERIAL_get(contents, Width, 4, 1, U_LE); @@ -6416,13 +6649,14 @@ int U_PMF_RECTF_get(const char **contents, U_FLOAT *X, U_FLOAT *Y, U_FLOAT *Widt \param Flags Record flags (bit U_PPF_C is referenced) \param Elements Number of rects to retrieve. \param Rects Caller must free. Array of U_PMF_RECTF coordinates. + \param blimit one byte past the end of data Rects in record may be either U_PMF_RECT or U_PMF_RECTF, but this function always returns U_PMF_RECTF */ -int U_PMF_VARRECTS_get(const char **contents, uint16_t Flags, int Elements, U_PMF_RECTF **Rects){ +int U_PMF_VARRECTS_get(const char **contents, uint16_t Flags, int Elements, U_PMF_RECTF **Rects, const char *blimit){ int16_t X16, Y16, Width, Height; - if(!contents || !*contents || !Rects){ return(0); } + if(!contents || !*contents || !Rects || !blimit){ return(0); } U_PMF_RECTF *rts = (U_PMF_RECTF *)malloc(Elements * sizeof(U_PMF_RECTF)); if(!rts){ *Rects = NULL; @@ -6430,16 +6664,28 @@ int U_PMF_VARRECTS_get(const char **contents, uint16_t Flags, int Elements, U_PM } *Rects = rts; + if(Flags & U_PPF_C){ + if(IS_MEM_UNSAFE(*contents, Elements*sizeof(U_PMF_RECT), blimit)){ + free(rts); + return(0); + } + } + else { + if(IS_MEM_UNSAFE(*contents, Elements*sizeof(U_PMF_RECT), blimit)){ + free(rts); + return(0); + } + } for(; Elements; Elements--, rts++){ if(Flags & U_PPF_C){ - (void) U_PMF_RECT_get(contents, &X16, &Y16, &Width, &Height); + (void) U_PMF_RECT_get(contents, &X16, &Y16, &Width, &Height, blimit); rts->X = X16; rts->Y = Y16; rts->Width = Width; rts->Height = Height; } else { - (void) U_PMF_RECTF_get(contents, &(rts->X), &(rts->Y), &(rts->Width), &(rts->Height)); + (void) U_PMF_RECTF_get(contents, &(rts->X), &(rts->Y), &(rts->Width), &(rts->Height), blimit); } } return(1); @@ -6451,13 +6697,18 @@ int U_PMF_VARRECTS_get(const char **contents, uint16_t Flags, int Elements, U_PM \param contents Record from which to extract data \param Type RegionNodeDataType \param Data Depending on Type: U_PMF_REGIONNODEPATH, U_PMF_RECTF, or U_PMF_REGIONNODECHILDNODES - + \param blimit one byte past the end of data EMF+ manual 2.2.2.40, Microsoft name: EmfPlusRegionNode Object + + Caller must check Data for possible memory access violations. */ -int U_PMF_REGIONNODE_get(const char *contents, uint32_t *Type, const char **Data){ - if(!contents || !Type|| !Data){ return(0); } +int U_PMF_REGIONNODE_get(const char *contents, uint32_t *Type, const char **Data, const char *blimit){ + if(!contents || !Type || !Data || !blimit){ return(0); } + /* Important! This only checks the constant part, the caller must check that returned data doesn't exceed blimit */ + if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_REGIONNODE), blimit))return(0); U_PMF_SERIAL_get(&contents, Type, 4, 1, U_LE); + if(contents >= blimit)return(0); // returned Data is variable size, this much at least must be true U_PMF_PTRSAV_COND(Data, contents, !(*Type == U_RNDT_Empty || *Type == U_RNDT_Infinite )); return(1); } @@ -6481,12 +6732,16 @@ int U_PMF_REGIONNODE_get(const char *contents, uint32_t *Type, const char **Data \param contents Record from which to extract data \param Size Bytes in Data \param Data Boundary of region node + \param blimit one byte past the end of data EMF+ manual 2.2.2.42, Microsoft name: EmfPlusRegionNodePath Object */ -int U_PMF_REGIONNODEPATH_get(const char *contents, int32_t *Size, const char **Data){ - if(!contents || !Size || !Data){ return(0); } +int U_PMF_REGIONNODEPATH_get(const char *contents, int32_t *Size, const char **Data, const char *blimit){ + if(!contents || !Size || !Data || !blimit){ return(0); } + /* Important! This only checks the constant part, the caller must check that returned data doesn't exceed blimit */ + if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_REGIONNODEPATH), blimit))return(0); U_PMF_SERIAL_get(&contents, Size, 4, 1, U_LE); + if(contents >= blimit)return(0); // returned Data is variable size, this much at least must be true U_PMF_PTRSAV_SHIFT(Data, &contents, 0); return(1); } @@ -6496,11 +6751,13 @@ int U_PMF_REGIONNODEPATH_get(const char *contents, int32_t *Size, const char **D \return 1 on success, 0 on error \param contents Record from which to extract data \param Color Color of brush + \param blimit one byte past the end of data EMF+ manual 2.2.2.43, Microsoft name: EmfPlusSolidBrushData Object */ -int U_PMF_SOLIDBRUSHDATA_get(const char *contents, U_PMF_ARGB *Color){ - if(!contents || !Color){ return(0); } +int U_PMF_SOLIDBRUSHDATA_get(const char *contents, U_PMF_ARGB *Color, const char *blimit){ + if(!contents || !Color || !blimit){ return(0); } + if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_SOLIDBRUSHDATA), blimit))return(0); U_PMF_SERIAL_get(&contents, Color, 4, 1, U_XE); return(1); } @@ -6513,12 +6770,14 @@ int U_PMF_SOLIDBRUSHDATA_get(const char *contents, U_PMF_ARGB *Color){ \param RangeCount Entries in CharRange array \param TabStops Array of tabstop locations \param CharRange Array of character ranges in the text + \param blimit one byte past the end of data EMF+ manual 2.2.2.44, Microsoft name: EmfPlusStringFormatData Object */ int U_PMF_STRINGFORMATDATA_get(const char *contents, uint32_t TabStopCount, uint32_t RangeCount, - const U_FLOAT **TabStops, const U_PMF_CHARACTERRANGE **CharRange){ - if(!contents || !TabStops|| !CharRange){ return(0); } + const U_FLOAT **TabStops, const U_PMF_CHARACTERRANGE **CharRange, const char *blimit){ + if(!contents || !TabStops|| !CharRange || !blimit){ return(0); } + if(IS_MEM_UNSAFE(contents, (TabStopCount + 2*RangeCount)*4, blimit))return(0); *TabStops = NULL; if(TabStopCount > 0){ U_PMF_SERIAL_get(&contents, TabStops, 4, TabStopCount, U_LE); } *CharRange = NULL; @@ -6533,13 +6792,19 @@ int U_PMF_STRINGFORMATDATA_get(const char *contents, uint32_t TabStopCount, uint \param Flags BrushData flags \param WrapMode WrapMode enumeration \param Data Optional texture data + \param blimit one byte past the end of data EMF+ manual 2.2.2.45, Microsoft name: EmfPlusTextureBrushData Object + + Caller must check Data for possible memory access violations. */ -int U_PMF_TEXTUREBRUSHDATA_get(const char *contents, uint32_t *Flags, int32_t *WrapMode, const char **Data){ - if(!contents || !Flags || !WrapMode || !Data){ return(0); } +int U_PMF_TEXTUREBRUSHDATA_get(const char *contents, uint32_t *Flags, int32_t *WrapMode, const char **Data, const char *blimit){ + if(!contents || !Flags || !WrapMode || !Data || !blimit){ return(0); } + /* Important! This only checks the constant part, the caller must check that returned data doesn't exceed blimit */ + if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_TEXTUREBRUSHDATA), blimit))return(0); U_PMF_SERIAL_get(&contents, Flags, 4, 1, U_LE); U_PMF_SERIAL_get(&contents, WrapMode, 4, 1, U_LE); + if(contents >= blimit)return(0); // returned Data is variable size, this much at least must be true U_PMF_PTRSAV_SHIFT(Data, &contents, 0); return(1); } @@ -6549,16 +6814,25 @@ int U_PMF_TEXTUREBRUSHDATA_get(const char *contents, uint32_t *Flags, int32_t *W \return 1 on success, 0 on error \param contents Record from which to extract data \param HasImage True if this object has an Image - \param Matrix Transformation matrix, present if Flag BrushDataTransform is set. + \param Matrix Transformation matrix, NULL if Flag BrushDataTransform is not set. \param Image Image that contains the texture. - + \param blimit one byte past the end of data EMF+ manual 2.2.2.46, Microsoft name: EmfPlusTextureBrushOptionalData Object + + Caller must check Image for possible memory access violations. */ -int U_PMF_TEXTUREBRUSHOPTIONALDATA_get(const char *contents, int HasImage, U_PMF_TRANSFORMMATRIX *Matrix, const char **Image){ - if(!contents || !Matrix || !Image){ return(0); } - U_PMF_SERIAL_get(&contents, Matrix, 4, 6, U_LE); - U_PMF_PTRSAV_COND(Image, contents, HasImage); +int U_PMF_TEXTUREBRUSHOPTIONALDATA_get(const char *contents, int HasImage, U_PMF_TRANSFORMMATRIX *Matrix, + const char **Image, const char *blimit){ + if(!contents || !Image || !blimit){ return(0); } + if(Matrix){ + if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_TRANSFORMMATRIX), blimit))return(0); + U_PMF_SERIAL_get(&contents, Matrix, 4, 6, U_LE); + } + if(HasImage){ + if(contents >= blimit)return(0); // returned Data is variable size, this much at least must be true + U_PMF_PTRSAV_COND(Image, contents, HasImage); + } return(1); } @@ -6567,11 +6841,13 @@ int U_PMF_TEXTUREBRUSHOPTIONALDATA_get(const char *contents, int HasImage, U_PMF \return 1 on success, 0 on error \param contents Record from which to extract data \param Matrix Transformation matrix, present if Flag BrushDataTransform is set. + \param blimit one byte past the end of data EMF+ manual 2.2.2.47, Microsoft name: EmfPlusTransformMatrix Object */ -int U_PMF_TRANSFORMMATRIX_get(const char *contents, U_PMF_TRANSFORMMATRIX *Matrix){ - if(!contents || !Matrix){ return(0); } +int U_PMF_TRANSFORMMATRIX_get(const char *contents, U_PMF_TRANSFORMMATRIX *Matrix, const char *blimit){ + if(!contents || !Matrix || !blimit){ return(0); } + if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_TRANSFORMMATRIX), blimit))return(0); U_PMF_SERIAL_get(&contents, Matrix, 4, 6, U_LE); return(1); } @@ -6582,11 +6858,13 @@ int U_PMF_TRANSFORMMATRIX_get(const char *contents, U_PMF_TRANSFORMMATRIX *Matri \param contents Record from which to extract data \param Radius Blur radius in pixels \param ExpandEdge 1: expand bitmap by Radius; 0: bitmap size unchanged + \param blimit one byte past the end of data EMF+ manual 2.2.3.1, Microsoft name: BlurEffect Object */ -int U_PMF_IE_BLUR_get(const char *contents, U_FLOAT *Radius, uint32_t *ExpandEdge){ - if(!contents || !Radius || !ExpandEdge){ return(0); } +int U_PMF_IE_BLUR_get(const char *contents, U_FLOAT *Radius, uint32_t *ExpandEdge, const char *blimit){ + if(!contents || !Radius || !ExpandEdge || !blimit){ return(0); } + if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_IE_BLUR), blimit))return(0); U_PMF_SERIAL_get(&contents, Radius, 4, 1, U_LE); U_PMF_SERIAL_get(&contents, ExpandEdge, 4, 1, U_LE); return(1); @@ -6598,11 +6876,13 @@ int U_PMF_IE_BLUR_get(const char *contents, U_FLOAT *Radius, uint32_t *ExpandEdg \param contents Record from which to extract data \param Brightness -255 to 255, 0 is unchanged, positive increases, negative decreases \param Contrast -100 to 100, 0 is unchanged, positive increases, negative decreases + \param blimit one byte past the end of data EMF+ manual 2.2.3.2, Microsoft name: BrightnessContrastEffect Object */ -int U_PMF_IE_BRIGHTNESSCONTRAST_get(const char *contents, int32_t *Brightness, int32_t *Contrast){ - if(!contents || !Brightness || !Contrast){ return(0); } +int U_PMF_IE_BRIGHTNESSCONTRAST_get(const char *contents, int32_t *Brightness, int32_t *Contrast, const char *blimit){ + if(!contents || !Brightness || !Contrast || !blimit){ return(0); } + if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_IE_BRIGHTNESSCONTRAST), blimit))return(0); U_PMF_SERIAL_get(&contents, Brightness, 4, 1, U_LE); U_PMF_SERIAL_get(&contents, Contrast, 4, 1, U_LE); return(1); @@ -6615,11 +6895,13 @@ int U_PMF_IE_BRIGHTNESSCONTRAST_get(const char *contents, int32_t *Brightness, i \param CyanRed -100 to 100, 0 is unchanged, positive increases Red & decreases Cyan, negative is opposite \param MagentaGreen -100 to 100, 0 is unchanged, positive increases Green & decreases Magenta, negative is opposite \param YellowBlue -100 to 100, 0 is unchanged, positive increases Blue & decreases Yellow, negative is opposite + \param blimit one byte past the end of data EMF+ manual 2.2.3.3, Microsoft name: ColorBalanceEffect Object */ -int U_PMF_IE_COLORBALANCE_get(const char *contents, int32_t *CyanRed, int32_t *MagentaGreen, int32_t *YellowBlue){ - if(!contents || !CyanRed || !MagentaGreen || !YellowBlue){ return(0); } +int U_PMF_IE_COLORBALANCE_get(const char *contents, int32_t *CyanRed, int32_t *MagentaGreen, int32_t *YellowBlue, const char *blimit){ + if(!contents || !CyanRed || !MagentaGreen || !YellowBlue || !blimit){ return(0); } + if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_IE_COLORBALANCE), blimit))return(0); U_PMF_SERIAL_get(&contents, CyanRed, 4, 1, U_LE); U_PMF_SERIAL_get(&contents, MagentaGreen, 4, 1, U_LE); U_PMF_SERIAL_get(&contents, YellowBlue, 4, 1, U_LE); @@ -6634,11 +6916,13 @@ int U_PMF_IE_COLORBALANCE_get(const char *contents, int32_t *CyanRed, int32_t *M \param Adjust CurveAdjustment enumeration \param Channel CurveChannel enumeration \param Intensity adjustment to apply. "Adjust" determines what field this is and range values. + \param blimit one byte past the end of data EMF+ manual 2.2.3.4, Microsoft name: ColorCurveEffect Object */ -int U_PMF_IE_COLORCURVE_get(const char *contents, uint32_t *Adjust, uint32_t *Channel, int32_t *Intensity){ - if(!contents || !Adjust || !Channel || !Intensity){ return(0); } +int U_PMF_IE_COLORCURVE_get(const char *contents, uint32_t *Adjust, uint32_t *Channel, int32_t *Intensity, const char *blimit){ + if(!contents || !Adjust || !Channel || !Intensity || !blimit){ return(0); } + if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_IE_COLORCURVE), blimit))return(0); U_PMF_SERIAL_get(&contents, Adjust, 4, 1, U_LE); U_PMF_SERIAL_get(&contents, Channel, 4, 1, U_LE); U_PMF_SERIAL_get(&contents, Intensity, 4, 1, U_LE); @@ -6653,12 +6937,14 @@ int U_PMF_IE_COLORCURVE_get(const char *contents, uint32_t *Adjust, uint32_t *Ch \param GLUT Green color lookup table \param RLUT Red color lookup table \param ALUT Alpha color lookup table + \param blimit one byte past the end of data EMF+ manual 2.2.3.5, Microsoft name: ColorLookupTableEffect Object */ int U_PMF_IE_COLORLOOKUPTABLE_get(const char *contents, - const uint8_t **BLUT, const uint8_t **GLUT, const uint8_t **RLUT, const uint8_t **ALUT){ - if(!contents || !BLUT || !GLUT || !RLUT || !ALUT){ return(0); } + const uint8_t **BLUT, const uint8_t **GLUT, const uint8_t **RLUT, const uint8_t **ALUT, const char *blimit){ + if(!contents || !BLUT || !GLUT || !RLUT || !ALUT || !blimit){ return(0); } + if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_IE_COLORLOOKUPTABLE) + 4 * 256, blimit))return(0); U_PMF_PTRSAV_SHIFT((const char **)BLUT, &contents, 256); U_PMF_PTRSAV_SHIFT((const char **)GLUT, &contents, 256); U_PMF_PTRSAV_SHIFT((const char **)RLUT, &contents, 256); @@ -6671,11 +6957,14 @@ int U_PMF_IE_COLORLOOKUPTABLE_get(const char *contents, \return 1 on success, 0 on error \param contents Record from which to extract data \param Matrix 5 x 5 color transformation matrix, First 4 rows are [{4 multiplier values},0.0] for R,G,B,A, last Row is [{4 color translation valuess}, 1.0] + \param blimit one byte past the end of data EMF+ manual 2.2.3.6, Microsoft name: ColorMatrixEffect Object */ -int U_PMF_IE_COLORMATRIX_get(const char *contents, U_PMF_IE_COLORMATRIX *Matrix){ - if(!contents || !Matrix){ return(0); } +int U_PMF_IE_COLORMATRIX_get(const char *contents, U_PMF_IE_COLORMATRIX *Matrix, const char *blimit){ + if(!contents || !Matrix || !blimit){ return(0); } + /* Important! This only checks the constant part, the caller must check that returned data doesn't exceed blimit */ + if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_IE_COLORMATRIX), blimit))return(0); U_PMF_SERIAL_get(&contents, Matrix, 4, 5*5, U_LE); return(1); } @@ -6687,11 +6976,13 @@ int U_PMF_IE_COLORMATRIX_get(const char *contents, U_PMF_IE_COLORMATRIX *Matrix) \param Hue -180 to 180, 0 is unchanged \param Saturation -100 to 100, 0 is unchanged \param Lightness -100 to 100, 0 is unchanged + \param blimit one byte past the end of data EMF+ manual 2.2.3.7, Microsoft name: HueSaturationLightnessEffect Object */ -int U_PMF_IE_HUESATURATIONLIGHTNESS_get(const char *contents, int32_t *Hue, int32_t *Saturation, int32_t *Lightness){ - if(!contents || !Hue || !Saturation || !Lightness){ return(0); } +int U_PMF_IE_HUESATURATIONLIGHTNESS_get(const char *contents, int32_t *Hue, int32_t *Saturation, int32_t *Lightness, const char *blimit){ + if(!contents || !Hue || !Saturation || !Lightness || !blimit){ return(0); } + if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_IE_HUESATURATIONLIGHTNESS), blimit))return(0); U_PMF_SERIAL_get(&contents, Hue, 4, 1, U_LE); U_PMF_SERIAL_get(&contents, Saturation, 4, 1, U_LE); U_PMF_SERIAL_get(&contents, Lightness, 4, 1, U_LE); @@ -6705,11 +6996,13 @@ int U_PMF_IE_HUESATURATIONLIGHTNESS_get(const char *contents, int32_t *Hue, int3 \param Highlight 0 to 100, 100 is unchanged \param Midtone -100 to 100, 0 is unchanged \param Shadow 0 to 100, 0 is unchanged + \param blimit one byte past the end of data EMF+ manual 2.2.3.8, Microsoft name: LevelsEffect Object */ -int U_PMF_IE_LEVELS_get(const char *contents, int32_t *Highlight, int32_t *Midtone, int32_t *Shadow){ - if(!contents || !Highlight || !Midtone || !Shadow){ return(0); } +int U_PMF_IE_LEVELS_get(const char *contents, int32_t *Highlight, int32_t *Midtone, int32_t *Shadow, const char *blimit){ + if(!contents || !Highlight || !Midtone || !Shadow || !blimit){ return(0); } + if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_IE_LEVELS), blimit))return(0); U_PMF_SERIAL_get(&contents, Highlight, 4, 1, U_LE); U_PMF_SERIAL_get(&contents, Midtone, 4, 1, U_LE); U_PMF_SERIAL_get(&contents, Shadow, 4, 1, U_LE); @@ -6722,12 +7015,15 @@ int U_PMF_IE_LEVELS_get(const char *contents, int32_t *Highlight, int32_t *Midto \param contents Record from which to extract data \param Elements Number of members in Rects \param Rects Caller must free. Pointer to memory holding an array of U_RECTL. + \param blimit one byte past the end of data EMF+ manual 2.2.3.9, Microsoft name: RedEyeCorrectionEffect Object */ -int U_PMF_IE_REDEYECORRECTION_get(const char *contents, int32_t *Elements, U_RECTL **Rects){ - if(!contents || !Elements || !Rects){ return(0); } +int U_PMF_IE_REDEYECORRECTION_get(const char *contents, int32_t *Elements, U_RECTL **Rects, const char *blimit){ + if(!contents || !Elements || !Rects || !blimit){ return(0); } + if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_IE_REDEYECORRECTION), blimit))return(0); U_PMF_SERIAL_get(&contents, Elements, 4, 1, U_LE); + if(IS_MEM_UNSAFE(contents, *Elements * 4, blimit))return(0); *Rects = (U_RECTL *) malloc(*Elements * sizeof(U_RECTL)); if(!*Rects){ return(0); } U_PMF_SERIAL_get(&contents, *Rects, 4, *Elements * 4, U_LE); @@ -6740,11 +7036,13 @@ int U_PMF_IE_REDEYECORRECTION_get(const char *contents, int32_t *Elements, U_REC \param contents Record from which to extract data \param Radius Sharpening radius in pixels \param Sharpen 0 to 100, 0 is unchanged + \param blimit one byte past the end of data EMF+ manual 2.2.3.10, Microsoft name: SharpenEffect Object */ -int U_PMF_IE_SHARPEN_get(const char *contents, U_FLOAT *Radius, int32_t *Sharpen){ - if(!contents || !Radius || !Sharpen){ return(0); } +int U_PMF_IE_SHARPEN_get(const char *contents, U_FLOAT *Radius, int32_t *Sharpen, const char *blimit){ + if(!contents || !Radius || !Sharpen || !blimit){ return(0); } + if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_IE_SHARPEN), blimit))return(0); U_PMF_SERIAL_get(&contents, Radius, 4, 1, U_LE); U_PMF_SERIAL_get(&contents, Sharpen, 4, 1, U_LE); return(1); @@ -6756,16 +7054,51 @@ int U_PMF_IE_SHARPEN_get(const char *contents, U_FLOAT *Radius, int32_t *Sharpen \param contents Record from which to extract data \param Hue -180 to 180, [positive==clockwise] rotation in degrees starting from blue \param Amount -100 [add black] to 100[add white], 0 is unchanged. Change in hue on specified axis + \param blimit one byte past the end of data EMF+ manual 2.2.3.11, Microsoft name: TintEffect Object */ -int U_PMF_IE_TINT_get(const char *contents, int32_t *Hue, int32_t *Amount){ - if(!contents || !Hue || !Amount){ return(0); } +int U_PMF_IE_TINT_get(const char *contents, int32_t *Hue, int32_t *Amount, const char *blimit){ + if(!contents || !Hue || !Amount || !blimit){ return(0); } + if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_IE_TINT), blimit))return(0); U_PMF_SERIAL_get(&contents, Hue, 4, 1, U_LE); U_PMF_SERIAL_get(&contents, Amount, 4, 1, U_LE); return(1); } +/* + + end of U_PMF_*_get() functions + ===================================================================================== + start of U_PMR_*_get() functions + + These functions all assume that the size field in the common EMF+ header has already + been checked, so that the extent the record claims exists in the data read in for the file. + Consequently none of them takes a blimit parameter. They generate a new one from the + header size field and contents if needed. + +*/ + +int U_PMR_common_stack_get(const char *contents, U_PMF_CMN_HDR *Header, uint32_t *StackID){ + if(!contents || !StackID){ return(0); } + + U_PMF_CMN_HDR lclHeader; + U_PMF_CMN_HDR_get(&contents, &lclHeader); + if(lclHeader.Size < sizeof(U_PMF_RESTORE))return(0); + if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); } + + U_PMF_SERIAL_get(&contents, StackID, 4, 1, U_LE); + return(1); +} + +/* for records that have a type but no associated flag bits or data */ +int U_PMR_common_header_get(const char *contents, U_PMF_CMN_HDR *Header){ + /* memory access safe, only uses the common header */ + if(!contents){ return(0); } + U_PMF_CMN_HDR_get(&contents, Header); + return(1); +} + /** \brief Get data from a U_PMR_OFFSETCLIP record \return 1 on success, 0 on error @@ -6779,7 +7112,12 @@ int U_PMF_IE_TINT_get(const char *contents, int32_t *Hue, int32_t *Amount){ int U_PMR_OFFSETCLIP_get(const char *contents, U_PMF_CMN_HDR *Header, U_FLOAT *dX, U_FLOAT *dY){ if(!contents){ return(0); } - U_PMF_CMN_HDR_get(&contents, Header); + + U_PMF_CMN_HDR lclHeader; + U_PMF_CMN_HDR_get(&contents, &lclHeader); + if(lclHeader.Size < sizeof(U_PMF_OFFSETCLIP))return(0); + if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); } + U_PMF_SERIAL_get(&contents, dX, 4, 1, U_LE); U_PMF_SERIAL_get(&contents, dY, 4, 1, U_LE); return(1); @@ -6812,10 +7150,14 @@ int U_PMR_RESETCLIP_get(const char *contents, U_PMF_CMN_HDR *Header){ int U_PMR_SETCLIPPATH_get(const char *contents, U_PMF_CMN_HDR *Header, uint32_t *PathID, int *CMenum){ if(!contents || !PathID || !CMenum){ return(0); } - uint16_t Flags = U_PMF_HEADERFLAGS_get(contents); - *CMenum = (Flags >> U_FF_SHFT_CM4) & U_FF_MASK_CM4; - *PathID = (Flags >> U_FF_SHFT_OID8) & U_FF_MASK_OID8; - U_PMF_CMN_HDR_get(&contents, Header); + + U_PMF_CMN_HDR lclHeader; + U_PMF_CMN_HDR_get(&contents, &lclHeader); + if(lclHeader.Size < sizeof(U_PMF_SETCLIPPATH))return(0); + if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); } + + *CMenum = (lclHeader.Flags >> U_FF_SHFT_CM4) & U_FF_MASK_CM4; + *PathID = (lclHeader.Flags >> U_FF_SHFT_OID8) & U_FF_MASK_OID8; return(1); } @@ -6833,9 +7175,13 @@ int U_PMR_SETCLIPRECT_get(const char *contents, U_PMF_CMN_HDR *Header, int *CMenum, U_PMF_RECTF *Rect){ if(!contents || !CMenum || !Rect ){ return(0); } - uint16_t Flags = U_PMF_HEADERFLAGS_get(contents); - *CMenum = (Flags >> U_FF_SHFT_CM4) & U_FF_MASK_CM4; - U_PMF_CMN_HDR_get(&contents, Header); + + U_PMF_CMN_HDR lclHeader; + U_PMF_CMN_HDR_get(&contents, &lclHeader); + if(lclHeader.Size < sizeof(U_PMF_SETCLIPRECT))return(0); + if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); } + + *CMenum = (lclHeader.Flags >> U_FF_SHFT_CM4) & U_FF_MASK_CM4; U_PMF_SERIAL_get(&contents, Rect, 4, 4, U_LE); return(1); } @@ -6853,10 +7199,14 @@ int U_PMR_SETCLIPRECT_get(const char *contents, U_PMF_CMN_HDR *Header, int U_PMR_SETCLIPREGION_get(const char *contents, U_PMF_CMN_HDR *Header, uint32_t *PathID, int *CMenum){ if(!contents || !PathID || !CMenum){ return(0); } - uint16_t Flags = U_PMF_HEADERFLAGS_get(contents); - *CMenum = (Flags >> U_FF_SHFT_CM4) & U_FF_MASK_CM4; - *PathID = (Flags >> U_FF_SHFT_OID8) & U_FF_MASK_OID8; - U_PMF_CMN_HDR_get(&contents, Header); + + U_PMF_CMN_HDR lclHeader; + U_PMF_CMN_HDR_get(&contents, &lclHeader); + if(lclHeader.Size < sizeof(U_PMF_SETCLIPREGION))return(0); + if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); } + + *CMenum = (lclHeader.Flags >> U_FF_SHFT_CM4) & U_FF_MASK_CM4; + *PathID = (lclHeader.Flags >> U_FF_SHFT_OID8) & U_FF_MASK_OID8; return(1); } @@ -6868,11 +7218,18 @@ int U_PMR_SETCLIPREGION_get(const char *contents, U_PMF_CMN_HDR *Header, \param Data Private data, may be anything EMF+ manual 2.3.2.1, Microsoft name: EmfPlusComment Record, Index 0x03 + + Caller must check Data for possible memory access violations. */ int U_PMR_COMMENT_get(const char *contents, U_PMF_CMN_HDR *Header, const char **Data){ if(!contents || !Data){ return(0); } - U_PMF_CMN_HDR_get(&contents, Header); + + U_PMF_CMN_HDR lclHeader; + U_PMF_CMN_HDR_get(&contents, &lclHeader); + if(lclHeader.Size < sizeof(U_PMF_COMMENT))return(0); + if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); } + U_PMF_PTRSAV_SHIFT(Data, &contents, 0); return(1); } @@ -6922,9 +7279,13 @@ int U_PMR_HEADER_get(const char *contents, U_PMF_CMN_HDR *Header, U_PMF_GRAPHICSVERSION *Version, int *IsDual, int *IsVideo, uint32_t *LogicalDpiX, uint32_t *LogicalDpiY){ if(!contents || !Version || !IsDual || !IsVideo || !LogicalDpiX || !LogicalDpiY){ return(0); } uint32_t tmp; - uint16_t Flags = U_PMF_HEADERFLAGS_get(contents); - *IsDual = (Flags & U_PPF_DM ? 1 : 0 ); - U_PMF_CMN_HDR_get(&contents, Header); + + U_PMF_CMN_HDR lclHeader; + U_PMF_CMN_HDR_get(&contents, &lclHeader); + if(lclHeader.Size < sizeof(U_PMF_HEADER))return(0); + if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); } + + *IsDual = (lclHeader.Flags & U_PPF_DM ? 1 : 0 ); U_PMF_SERIAL_get(&contents, Version, 4, 1, U_LE); U_PMF_SERIAL_get(&contents, &tmp, 4, 1, U_LE); U_PMF_SERIAL_get(&contents, LogicalDpiX, 4, 1, U_LE); @@ -6945,7 +7306,12 @@ int U_PMR_HEADER_get(const char *contents, U_PMF_CMN_HDR *Header, int U_PMR_CLEAR_get(const char *contents, U_PMF_CMN_HDR *Header, U_PMF_ARGB *Color){ if(!contents || !Color){ return(0); } - U_PMF_CMN_HDR_get(&contents, Header); + + U_PMF_CMN_HDR lclHeader; + U_PMF_CMN_HDR_get(&contents, &lclHeader); + if(lclHeader.Size < sizeof(U_PMF_CLEAR))return(0); + if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); } + U_PMF_SERIAL_get(&contents, Color, 4, 1, U_LE); return(1); } @@ -6968,16 +7334,26 @@ int U_PMR_DRAWARC_get(const char *contents, U_PMF_CMN_HDR *Header, U_FLOAT *Start, U_FLOAT *Sweep, U_PMF_RECTF *Rect){ if(!contents || !PenID || !ctype || !Start || !Sweep || !Rect){ return(0); } - uint16_t Flags = U_PMF_HEADERFLAGS_get(contents); - *ctype = (Flags & U_PPF_C ? 1 : 0 ); - *PenID = (Flags >> U_FF_SHFT_OID8) & U_FF_MASK_OID8; - U_PMF_CMN_HDR_get(&contents, Header); + + const char *blimit = contents; + U_PMF_CMN_HDR lclHeader; + U_PMF_CMN_HDR_get(&contents, &lclHeader); + if(lclHeader.Size < sizeof(U_PMF_DRAWARC))return(0); + if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); } + blimit += lclHeader.Size; + + *ctype = (lclHeader.Flags & U_PPF_C ? 1 : 0 ); + *PenID = (lclHeader.Flags >> U_FF_SHFT_OID8) & U_FF_MASK_OID8; U_PMF_SERIAL_get(&contents, Start, 4, 1, U_LE); U_PMF_SERIAL_get(&contents, Sweep, 4, 1, U_LE); - U_PMF_SERIAL_get(&contents, Rect, 4, 4, U_LE); + U_PMF_RECTF *Rects = NULL; + if(!U_PMF_VARRECTS_get(&contents, lclHeader.Flags, 1, &Rects, blimit))return(0); + memcpy(Rect,Rects,sizeof(U_PMF_RECTF)); + free(Rects); return(1); } + /** \brief Get data from a U_PMR_DRAWBEZIERS record \return 1 on success, 0 on error @@ -6996,14 +7372,20 @@ int U_PMR_DRAWBEZIERS_get(const char *contents, U_PMF_CMN_HDR *Header, uint32_t *Elements, U_PMF_POINTF **Points){ if(!contents || !PenID || !ctype || !RelAbs || !Elements || !Points){ return(0); } - uint16_t Flags = U_PMF_HEADERFLAGS_get(contents); - *ctype = (Flags & U_PPF_C ? 1 : 0 ); - *RelAbs = (Flags & U_PPF_P ? 1 : 0 ); - *PenID = (Flags >> U_FF_SHFT_OID8) & U_FF_MASK_OID8; - U_PMF_CMN_HDR_get(&contents, Header); + + const char *blimit = contents; + U_PMF_CMN_HDR lclHeader; + U_PMF_CMN_HDR_get(&contents, &lclHeader); + if(lclHeader.Size < sizeof(U_PMF_DRAWBEZIERS))return(0); + if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); } + blimit += lclHeader.Size; + + *ctype = (lclHeader.Flags & U_PPF_C ? 1 : 0 ); + *RelAbs = (lclHeader.Flags & U_PPF_P ? 1 : 0 ); + *PenID = (lclHeader.Flags >> U_FF_SHFT_OID8) & U_FF_MASK_OID8; U_PMF_SERIAL_get(&contents, Elements, 4, 1, U_LE); - U_PMF_VARPOINTS_get(&contents, Flags, *Elements, Points); - return(1); + int status = U_PMF_VARPOINTS_get(contents, lclHeader.Flags, *Elements, Points, blimit ); + return(status); } /** @@ -7025,14 +7407,20 @@ int U_PMR_DRAWCLOSEDCURVE_get(const char *contents, U_PMF_CMN_HDR *Header, U_FLOAT *Tension, uint32_t *Elements, U_PMF_POINTF **Points){ if(!contents || !PenID || !ctype || !RelAbs || !Tension || !Elements || !Points){ return(0); } - uint16_t Flags = U_PMF_HEADERFLAGS_get(contents); - *ctype = (Flags & U_PPF_C ? 1 : 0 ); - *RelAbs = (Flags & U_PPF_P ? 1 : 0 ); - *PenID = (Flags >> U_FF_SHFT_OID8) & U_FF_MASK_OID8; - U_PMF_CMN_HDR_get(&contents, Header); + + const char *blimit = contents; + U_PMF_CMN_HDR lclHeader; + U_PMF_CMN_HDR_get(&contents, &lclHeader); + if(lclHeader.Size < sizeof(U_PMF_DRAWCLOSEDCURVE))return(0); + if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); } + blimit += lclHeader.Size; + + *ctype = (lclHeader.Flags & U_PPF_C ? 1 : 0 ); + *RelAbs = (lclHeader.Flags & U_PPF_P ? 1 : 0 ); + *PenID = (lclHeader.Flags >> U_FF_SHFT_OID8) & U_FF_MASK_OID8; U_PMF_SERIAL_get(&contents, Tension, 4, 1, U_LE); U_PMF_SERIAL_get(&contents, Elements, 4, 1, U_LE); - U_PMF_VARPOINTS_get(&contents, Flags, *Elements, Points); + U_PMF_VARPOINTS_get(contents, lclHeader.Flags, *Elements, Points, blimit); return(1); } @@ -7056,15 +7444,21 @@ int U_PMR_DRAWCURVE_get(const char *contents, U_PMF_CMN_HDR *Header, U_FLOAT *Tension, uint32_t *Offset, uint32_t *NSegs, uint32_t *Elements, U_PMF_POINTF **Points){ if(!contents || !PenID || !ctype || !Tension || !Offset || !NSegs || !Elements || !Points){ return(0); } - uint16_t Flags = U_PMF_HEADERFLAGS_get(contents); - *ctype = (Flags & U_PPF_C ? 1 : 0 ); - *PenID = (Flags >> U_FF_SHFT_OID8) & U_FF_MASK_OID8; - U_PMF_CMN_HDR_get(&contents, Header); + + const char *blimit = contents; + U_PMF_CMN_HDR lclHeader; + U_PMF_CMN_HDR_get(&contents, &lclHeader); + if(lclHeader.Size < sizeof(U_PMF_DRAWCURVE))return(0); + if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); } + blimit += lclHeader.Size; + + *ctype = (lclHeader.Flags & U_PPF_C ? 1 : 0 ); + *PenID = (lclHeader.Flags >> U_FF_SHFT_OID8) & U_FF_MASK_OID8; U_PMF_SERIAL_get(&contents, Tension, 4, 1, U_LE); U_PMF_SERIAL_get(&contents, Offset, 4, 1, U_LE); U_PMF_SERIAL_get(&contents, NSegs, 4, 1, U_LE); U_PMF_SERIAL_get(&contents, Elements, 4, 1, U_LE); - U_PMF_VARPOINTS_get(&contents, Flags, *Elements, Points); + U_PMF_VARPOINTS_get(contents, lclHeader.Flags, *Elements, Points, blimit); return(1); } @@ -7092,15 +7486,22 @@ int U_PMR_DRAWDRIVERSTRING_get(const char *contents, U_PMF_CMN_HDR *Header, uint16_t **Glyphs, U_PMF_POINTF **Points, U_PMF_TRANSFORMMATRIX **Matrix){ if(!contents || !FontID || !btype || !Tension || !BrushID || !DSOFlags || !HasMatrix || !Elements || !Glyphs || !Points || !Matrix){ return(0); } - uint16_t Flags = U_PMF_HEADERFLAGS_get(contents); - *btype = (Flags & U_PPF_B ? 1 : 0 ); - *FontID = (Flags >> U_FF_SHFT_OID8) & U_FF_MASK_OID8; - U_PMF_CMN_HDR_get(&contents, Header); + + const char *blimit = contents; + U_PMF_CMN_HDR lclHeader; + U_PMF_CMN_HDR_get(&contents, &lclHeader); + if(lclHeader.Size < sizeof(U_PMF_DRAWDRIVERSTRING))return(0); + if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); } + blimit += lclHeader.Size; + + *btype = (lclHeader.Flags & U_PPF_B ? 1 : 0 ); + *FontID = (lclHeader.Flags >> U_FF_SHFT_OID8) & U_FF_MASK_OID8; U_PMF_SERIAL_get(&contents, Tension, 4, 1, U_LE); U_PMF_SERIAL_get(&contents, BrushID, 4, 1, (*btype ? U_XE : U_LE)); /* color is not byte swapped, ID integer is */ U_PMF_SERIAL_get(&contents, DSOFlags, 4, 1, U_LE); U_PMF_SERIAL_get(&contents, HasMatrix, 4, 1, U_LE); U_PMF_SERIAL_get(&contents, Elements, 4, 1, U_LE); + if(IS_MEM_UNSAFE(contents, *Elements*2 + *Elements*2*4 + 24, blimit))return(0); if(!U_PMF_SERIAL_array_copy_get(&contents, (void **)Glyphs, 2, *Elements, U_LE, (*DSOFlags & U_DSO_CmapLookup))){ return(0); } if(!U_PMF_SERIAL_array_copy_get(&contents, (void **)Points, 4, *Elements *2, U_LE, (*DSOFlags & U_DSO_RealizedAdvance))){ return(0); } if(!U_PMF_SERIAL_array_copy_get(&contents, (void **)Matrix, 4, 6, U_LE, (*HasMatrix))){ return(0); } @@ -7122,10 +7523,14 @@ int U_PMR_DRAWELLIPSE_get(const char *contents, U_PMF_CMN_HDR *Header, uint32_t *PenID, int *ctype, U_PMF_RECTF *Rect){ if(!contents || !PenID || !ctype || !Rect){ return(0); } - uint16_t Flags = U_PMF_HEADERFLAGS_get(contents); - *ctype = (Flags & U_PPF_C ? 1 : 0 ); - *PenID = (Flags >> U_FF_SHFT_OID8) & U_FF_MASK_OID8; - U_PMF_CMN_HDR_get(&contents, Header); + + U_PMF_CMN_HDR lclHeader; + U_PMF_CMN_HDR_get(&contents, &lclHeader); + if(lclHeader.Size < sizeof(U_PMF_DRAWELLIPSE))return(0); + if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); } + + *ctype = (lclHeader.Flags & U_PPF_C ? 1 : 0 ); + *PenID = (lclHeader.Flags >> U_FF_SHFT_OID8) & U_FF_MASK_OID8; U_PMF_SERIAL_get(&contents, Rect, 4, 4, U_LE); return(1); } @@ -7149,10 +7554,14 @@ int U_PMR_DRAWIMAGE_get(const char *contents, U_PMF_CMN_HDR *Header, uint32_t *ImgAttrID, int32_t *SrcUnit, U_PMF_RECTF *SrcRect, U_PMF_RECTF *DstRect){ if(!contents || !ImgID || !ctype || !ImgAttrID || !SrcUnit || !SrcRect || !DstRect){ return(0); } - uint16_t Flags = U_PMF_HEADERFLAGS_get(contents); - *ctype = (Flags & U_PPF_C ? 1 : 0 ); - *ImgID = (Flags >> U_FF_SHFT_OID8) & U_FF_MASK_OID8; - U_PMF_CMN_HDR_get(&contents, Header); + + U_PMF_CMN_HDR lclHeader; + U_PMF_CMN_HDR_get(&contents, &lclHeader); + if(lclHeader.Size < sizeof(U_PMF_DRAWIMAGE))return(0); + if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); } + + *ctype = (lclHeader.Flags & U_PPF_C ? 1 : 0 ); + *ImgID = (lclHeader.Flags >> U_FF_SHFT_OID8) & U_FF_MASK_OID8; U_PMF_SERIAL_get(&contents, ImgAttrID, 4, 1, U_LE); U_PMF_SERIAL_get(&contents, SrcUnit, 4, 1, U_LE); U_PMF_SERIAL_get(&contents, SrcRect, 4, 4, U_LE); @@ -7182,17 +7591,23 @@ int U_PMR_DRAWIMAGEPOINTS_get(const char *contents, U_PMF_CMN_HDR *Header, uint32_t *ImgAttrID, int32_t *SrcUnit, U_PMF_RECTF *SrcRect, uint32_t *Elements, U_PMF_POINTF **Points){ if(!contents || !ImgID || !ctype || !etype || !RelAbs || !ImgAttrID || !SrcUnit || !Elements || !Points){ return(0); } - uint16_t Flags = U_PMF_HEADERFLAGS_get(contents); - *ctype = (Flags & U_PPF_C ? 1 : 0 ); - *etype = (Flags & U_PPF_E ? 1 : 0 ); - *RelAbs = (Flags & U_PPF_P ? 1 : 0 ); - *ImgID = (Flags >> U_FF_SHFT_OID8) & U_FF_MASK_OID8; - U_PMF_CMN_HDR_get(&contents, Header); + + const char *blimit = contents; + U_PMF_CMN_HDR lclHeader; + U_PMF_CMN_HDR_get(&contents, &lclHeader); + if(lclHeader.Size < sizeof(U_PMF_DRAWIMAGEPOINTS))return(0); + if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); } + blimit += lclHeader.Size; + + *ctype = (lclHeader.Flags & U_PPF_C ? 1 : 0 ); + *etype = (lclHeader.Flags & U_PPF_E ? 1 : 0 ); + *RelAbs = (lclHeader.Flags & U_PPF_P ? 1 : 0 ); + *ImgID = (lclHeader.Flags >> U_FF_SHFT_OID8) & U_FF_MASK_OID8; U_PMF_SERIAL_get(&contents, ImgAttrID, 4, 1, U_LE); U_PMF_SERIAL_get(&contents, SrcUnit, 4, 1, U_LE); U_PMF_SERIAL_get(&contents, SrcRect, 4, 4, U_LE); U_PMF_SERIAL_get(&contents, Elements, 4, 1, U_LE); - U_PMF_VARPOINTS_get(&contents, Flags, *Elements, Points); + U_PMF_VARPOINTS_get(contents, lclHeader.Flags, *Elements, Points, blimit); return(1); } @@ -7215,14 +7630,20 @@ int U_PMR_DRAWLINES_get(const char *contents, U_PMF_CMN_HDR *Header, uint32_t *Elements, U_PMF_POINTF **Points){ if(!contents || !PenID || !ctype || !dtype || !RelAbs || !Elements || !Points){ return(0); } - uint16_t Flags = U_PMF_HEADERFLAGS_get(contents); - *ctype = (Flags & U_PPF_C ? 1 : 0 ); - *dtype = (Flags & U_PPF_D ? 1 : 0 ); - *RelAbs = (Flags & U_PPF_P ? 1 : 0 ); - *PenID = (Flags >> U_FF_SHFT_OID8) & U_FF_MASK_OID8; - U_PMF_CMN_HDR_get(&contents, Header); + + const char *blimit = contents; + U_PMF_CMN_HDR lclHeader; + U_PMF_CMN_HDR_get(&contents, &lclHeader); + if(lclHeader.Size < sizeof(U_PMF_DRAWLINES))return(0); + if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); } + blimit += lclHeader.Size; + + *ctype = (lclHeader.Flags & U_PPF_C ? 1 : 0 ); + *dtype = (lclHeader.Flags & U_PPF_D ? 1 : 0 ); + *RelAbs = (lclHeader.Flags & U_PPF_P ? 1 : 0 ); + *PenID = (lclHeader.Flags >> U_FF_SHFT_OID8) & U_FF_MASK_OID8; U_PMF_SERIAL_get(&contents, Elements, 4, 1, U_LE); - U_PMF_VARPOINTS_get(&contents, Flags, *Elements, Points); + U_PMF_VARPOINTS_get(contents, lclHeader.Flags, *Elements, Points, blimit); return(1); } @@ -7239,9 +7660,13 @@ int U_PMR_DRAWLINES_get(const char *contents, U_PMF_CMN_HDR *Header, int U_PMR_DRAWPATH_get(const char *contents, U_PMF_CMN_HDR *Header, uint32_t *PathID, uint32_t *PenID){ if(!contents || !PathID || !PenID){ return(0); } - uint16_t Flags = U_PMF_HEADERFLAGS_get(contents); - *PathID = (Flags >> U_FF_SHFT_OID8) & U_FF_MASK_OID8; - U_PMF_CMN_HDR_get(&contents, Header); + + U_PMF_CMN_HDR lclHeader; + U_PMF_CMN_HDR_get(&contents, &lclHeader); + if(lclHeader.Size < sizeof(U_PMF_DRAWPATH))return(0); + if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); } + + *PathID = (lclHeader.Flags >> U_FF_SHFT_OID8) & U_FF_MASK_OID8; U_PMF_SERIAL_get(&contents, PenID, 4, 1, U_LE); return(1); } @@ -7264,13 +7689,22 @@ int U_PMR_DRAWPIE_get(const char *contents, U_PMF_CMN_HDR *Header, U_FLOAT *Start, U_FLOAT *Sweep, U_PMF_RECTF *Rect){ if(!contents || !PenID || !ctype || !Start || !Sweep || !Rect){ return(0); } - uint16_t Flags = U_PMF_HEADERFLAGS_get(contents); - *ctype = (Flags & U_PPF_C ? 1 : 0 ); - *PenID = (Flags >> U_FF_SHFT_OID8) & U_FF_MASK_OID8; - U_PMF_CMN_HDR_get(&contents, Header); + + const char *blimit = contents; + U_PMF_CMN_HDR lclHeader; + U_PMF_CMN_HDR_get(&contents, &lclHeader); + if(lclHeader.Size < sizeof(U_PMF_DRAWPIE))return(0); + if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); } + blimit += lclHeader.Size; + + *ctype = (lclHeader.Flags & U_PPF_C ? 1 : 0 ); + *PenID = (lclHeader.Flags >> U_FF_SHFT_OID8) & U_FF_MASK_OID8; U_PMF_SERIAL_get(&contents, Start, 4, 1, U_LE); U_PMF_SERIAL_get(&contents, Sweep, 4, 1, U_LE); - U_PMF_SERIAL_get(&contents, Rect, 4, 4, U_LE); + U_PMF_RECTF *Rects = NULL; + if(!U_PMF_VARRECTS_get(&contents, lclHeader.Flags, 1, &Rects, blimit))return(0); + memcpy(Rect,Rects,sizeof(U_PMF_RECTF)); + free(Rects); return(1); } @@ -7294,12 +7728,18 @@ int U_PMR_DRAWRECTS_get(const char *contents, U_PMF_CMN_HDR *Header, uint32_t *Elements, U_PMF_RECTF **Rects){ if(!contents || !PenID || !Elements || !Rects){ return(0); } - uint16_t Flags = U_PMF_HEADERFLAGS_get(contents); - *PenID = (Flags >> U_FF_SHFT_OID8) & U_FF_MASK_OID8; - *ctype = (Flags & U_PPF_C ? 1 : 0 ); - U_PMF_CMN_HDR_get(&contents, Header); + + const char *blimit = contents; + U_PMF_CMN_HDR lclHeader; + U_PMF_CMN_HDR_get(&contents, &lclHeader); + if(lclHeader.Size < sizeof(U_PMF_DRAWPIE))return(0); + if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); } + blimit += lclHeader.Size; + + *PenID = (lclHeader.Flags >> U_FF_SHFT_OID8) & U_FF_MASK_OID8; + *ctype = (lclHeader.Flags & U_PPF_C ? 1 : 0 ); U_PMF_SERIAL_get(&contents, Elements, 4, 1, U_LE); - U_PMF_VARRECTS_get(&contents, Flags, *Elements, Rects); + U_PMF_VARRECTS_get(&contents, lclHeader.Flags, *Elements, Rects, blimit); return(1); } @@ -7323,14 +7763,21 @@ int U_PMR_DRAWSTRING_get(const char *contents, U_PMF_CMN_HDR *Header, uint32_t *BrushID, uint32_t *FormatID, uint32_t *Elements, U_PMF_RECTF *Rect, uint16_t **String){ if(!contents || !FontID || !btype || !BrushID || !FormatID || !Elements || !String){ return(0); } - uint16_t Flags = U_PMF_HEADERFLAGS_get(contents); - *btype = (Flags & U_PPF_B ? 1 : 0 ); - *FontID = (Flags >> U_FF_SHFT_OID8) & U_FF_MASK_OID8; - U_PMF_CMN_HDR_get(&contents, Header); + + const char *blimit = contents; + U_PMF_CMN_HDR lclHeader; + U_PMF_CMN_HDR_get(&contents, &lclHeader); + if(lclHeader.Size < sizeof(U_PMF_DRAWPIE))return(0); + if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); } + blimit += lclHeader.Size; + + *btype = (lclHeader.Flags & U_PPF_B ? 1 : 0 ); + *FontID = (lclHeader.Flags >> U_FF_SHFT_OID8) & U_FF_MASK_OID8; U_PMF_SERIAL_get(&contents, BrushID, 4, 1, (*btype ? U_XE : U_LE)); /* color is not byte swapped, ID integer is */ U_PMF_SERIAL_get(&contents, FormatID, 4, 1, U_LE); U_PMF_SERIAL_get(&contents, Elements, 4, 1, U_LE); U_PMF_SERIAL_get(&contents, Rect, 4, 4, U_LE); + if(IS_MEM_UNSAFE(contents, *Elements * 2, blimit))return(0); if(!U_PMF_SERIAL_array_copy_get(&contents, (void **)String, 2, *Elements, U_XE, 1)){ return(0); } return(1); } @@ -7356,16 +7803,22 @@ int U_PMR_FILLCLOSEDCURVE_get(const char *contents, U_PMF_CMN_HDR *Header, uint32_t *BrushID, U_FLOAT *Tension, uint32_t *Elements, U_PMF_POINTF **Points){ if(!contents || !btype || !ctype || !ftype || !RelAbs || !BrushID || !Tension || !Elements || !Points){ return(0); } - uint16_t Flags = U_PMF_HEADERFLAGS_get(contents); - *btype = (Flags & U_PPF_B ? 1 : 0 ); - *ctype = (Flags & U_PPF_C ? 1 : 0 ); - *ftype = (Flags & U_PPF_F ? 1 : 0 ); - *RelAbs = (Flags & U_PPF_P ? 1 : 0 ); - U_PMF_CMN_HDR_get(&contents, Header); + + const char *blimit = contents; + U_PMF_CMN_HDR lclHeader; + U_PMF_CMN_HDR_get(&contents, &lclHeader); + if(lclHeader.Size < sizeof(U_PMF_DRAWLINES))return(0); + if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); } + blimit += lclHeader.Size; + + *btype = (lclHeader.Flags & U_PPF_B ? 1 : 0 ); + *ctype = (lclHeader.Flags & U_PPF_C ? 1 : 0 ); + *ftype = (lclHeader.Flags & U_PPF_F ? 1 : 0 ); + *RelAbs = (lclHeader.Flags & U_PPF_P ? 1 : 0 ); U_PMF_SERIAL_get(&contents, BrushID, 4, 1, (*btype ? U_XE : U_LE)); /* color is not byte swapped, ID integer is */ U_PMF_SERIAL_get(&contents, Tension, 4, 1, U_LE); U_PMF_SERIAL_get(&contents, Elements, 4, 1, U_LE); - U_PMF_VARPOINTS_get(&contents, Flags, *Elements, Points); + U_PMF_VARPOINTS_get(contents, lclHeader.Flags, *Elements, Points, blimit); return(1); } @@ -7386,10 +7839,14 @@ int U_PMR_FILLELLIPSE_get(const char *contents, U_PMF_CMN_HDR *Header, uint32_t *BrushID, U_PMF_RECTF *Rect){ if(!contents || !btype || !ctype || !BrushID || !Rect){ return(0); } - uint16_t Flags = U_PMF_HEADERFLAGS_get(contents); - *btype = (Flags & U_PPF_B ? 1 : 0 ); - *ctype = (Flags & U_PPF_C ? 1 : 0 ); - U_PMF_CMN_HDR_get(&contents, Header); + + U_PMF_CMN_HDR lclHeader; + U_PMF_CMN_HDR_get(&contents, &lclHeader); + if(lclHeader.Size < sizeof(U_PMF_FILLELLIPSE))return(0); + if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); } + + *btype = (lclHeader.Flags & U_PPF_B ? 1 : 0 ); + *ctype = (lclHeader.Flags & U_PPF_C ? 1 : 0 ); U_PMF_SERIAL_get(&contents, BrushID, 4, 1, (*btype ? U_XE : U_LE)); /* color is not byte swapped, ID integer is */ U_PMF_SERIAL_get(&contents, Rect, 4, 4, U_LE); return(1); @@ -7412,10 +7869,14 @@ int U_PMR_FILLPATH_get(const char *contents, U_PMF_CMN_HDR *Header, uint32_t *PathID, int *btype, uint32_t *BrushID){ if(!contents || !PathID || !btype || !BrushID){ return(0); } - uint16_t Flags = U_PMF_HEADERFLAGS_get(contents); - *btype = (Flags & U_PPF_B ? 1 : 0 ); - *PathID = (Flags >> U_FF_SHFT_OID8) & U_FF_MASK_OID8; - U_PMF_CMN_HDR_get(&contents, Header); + + U_PMF_CMN_HDR lclHeader; + U_PMF_CMN_HDR_get(&contents, &lclHeader); + if(lclHeader.Size < sizeof(U_PMF_FILLPATH))return(0); + if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); } + + *btype = (lclHeader.Flags & U_PPF_B ? 1 : 0 ); + *PathID = (lclHeader.Flags >> U_FF_SHFT_OID8) & U_FF_MASK_OID8; U_PMF_SERIAL_get(&contents, BrushID, 4, 1, (*btype ? U_XE : U_LE)); /* color is not byte swapped, ID integer is */ return(1); } @@ -7439,14 +7900,23 @@ int U_PMR_FILLPIE_get(const char *contents, U_PMF_CMN_HDR *Header, uint32_t *BrushID, U_FLOAT *Start, U_FLOAT *Sweep, U_PMF_RECTF *Rect){ if(!contents || !btype || !ctype || !BrushID || !Start || !Sweep || !Rect){ return(0); } - uint16_t Flags = U_PMF_HEADERFLAGS_get(contents); - *btype = (Flags & U_PPF_B ? 1 : 0 ); - *ctype = (Flags & U_PPF_C ? 1 : 0 ); - U_PMF_CMN_HDR_get(&contents, Header); + + const char *blimit = contents; + U_PMF_CMN_HDR lclHeader; + U_PMF_CMN_HDR_get(&contents, &lclHeader); + if(lclHeader.Size < sizeof(U_PMF_FILLPIE))return(0); + if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); } + blimit += lclHeader.Size; + + *btype = (lclHeader.Flags & U_PPF_B ? 1 : 0 ); + *ctype = (lclHeader.Flags & U_PPF_C ? 1 : 0 ); U_PMF_SERIAL_get(&contents, BrushID, 4, 1, (*btype ? U_XE : U_LE)); /* color is not byte swapped, ID integer is */ U_PMF_SERIAL_get(&contents, Start, 4, 1, U_LE); U_PMF_SERIAL_get(&contents, Sweep, 4, 1, U_LE); - U_PMF_SERIAL_get(&contents, Rect, 4, 4, U_LE); + U_PMF_RECTF *Rects = NULL; + if(!U_PMF_VARRECTS_get(&contents, lclHeader.Flags, 1, &Rects, blimit))return(0); + memcpy(Rect,Rects,sizeof(U_PMF_RECTF)); + free(Rects); return(1); } @@ -7469,14 +7939,20 @@ int U_PMR_FILLPOLYGON_get(const char *contents, U_PMF_CMN_HDR *Header, uint32_t *BrushID, uint32_t *Elements, U_PMF_POINTF **Points){ if(!contents || !btype || !ctype || !RelAbs || !BrushID || !Elements || !Points){ return(0); } - uint16_t Flags = U_PMF_HEADERFLAGS_get(contents); - *btype = (Flags & U_PPF_B ? 1 : 0 ); - *ctype = (Flags & U_PPF_C ? 1 : 0 ); - *RelAbs = (Flags & U_PPF_R ? 1 : 0 ); - U_PMF_CMN_HDR_get(&contents, Header); + + const char *blimit = contents; + U_PMF_CMN_HDR lclHeader; + U_PMF_CMN_HDR_get(&contents, &lclHeader); + if(lclHeader.Size < sizeof(U_PMF_DRAWLINES))return(0); + if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); } + blimit += lclHeader.Size; + + *btype = (lclHeader.Flags & U_PPF_B ? 1 : 0 ); + *ctype = (lclHeader.Flags & U_PPF_C ? 1 : 0 ); + *RelAbs = (lclHeader.Flags & U_PPF_R ? 1 : 0 ); U_PMF_SERIAL_get(&contents, BrushID, 4, 1, (*btype ? U_XE : U_LE)); /* color is not byte swapped, ID integer is */ U_PMF_SERIAL_get(&contents, Elements, 4, 1, U_LE); - U_PMF_VARPOINTS_get(&contents, Flags, *Elements, Points); + U_PMF_VARPOINTS_get(contents, lclHeader.Flags, *Elements, Points, blimit); return(1); } @@ -7505,13 +7981,19 @@ int U_PMR_FILLRECTS_get(const char *contents, U_PMF_CMN_HDR *Header, uint32_t *BrushID, uint32_t *Elements, U_PMF_RECTF **Rects){ if(!contents || !btype || !ctype || !BrushID || !Elements || !Rects){ return(0); } - uint16_t Flags = U_PMF_HEADERFLAGS_get(contents); - *btype = (Flags & U_PPF_B ? 1 : 0 ); - *ctype = (Flags & U_PPF_C ? 1 : 0 ); - U_PMF_CMN_HDR_get(&contents, Header); + + const char *blimit = contents; + U_PMF_CMN_HDR lclHeader; + U_PMF_CMN_HDR_get(&contents, &lclHeader); + if(lclHeader.Size < sizeof(U_PMF_FILLRECTS))return(0); + if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); } + blimit += lclHeader.Size; + + *btype = (lclHeader.Flags & U_PPF_B ? 1 : 0 ); + *ctype = (lclHeader.Flags & U_PPF_C ? 1 : 0 ); U_PMF_SERIAL_get(&contents, BrushID, 4, 1, (*btype ? U_XE : U_LE)); /* color is not byte swapped, ID integer is */ U_PMF_SERIAL_get(&contents, Elements, 4, 1, U_LE); - U_PMF_VARRECTS_get(&contents, Flags, *Elements, Rects); + U_PMF_VARRECTS_get(&contents, lclHeader.Flags, *Elements, Rects, blimit); /* correct btype, if necessary, for invalid EMF+ input */ if((*BrushID > 63) & !*btype)*btype=1; return(1); @@ -7534,11 +8016,15 @@ int U_PMR_FILLREGION_get(const char *contents, U_PMF_CMN_HDR *Header, uint32_t *RgnID, int *btype, int *ctype, uint32_t *BrushID){ if(!contents || !RgnID || !btype || !ctype || !BrushID){ return(0); } - uint16_t Flags = U_PMF_HEADERFLAGS_get(contents); - *btype = (Flags & U_PPF_B ? 1 : 0 ); - *ctype = (Flags & U_PPF_C ? 1 : 0 ); - *RgnID = (Flags >> U_FF_SHFT_OID8) & U_FF_MASK_OID8; - U_PMF_CMN_HDR_get(&contents, Header); + + U_PMF_CMN_HDR lclHeader; + U_PMF_CMN_HDR_get(&contents, &lclHeader); + if(lclHeader.Size < sizeof(U_PMF_FILLREGION))return(0); + if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); } + + *btype = (lclHeader.Flags & U_PPF_B ? 1 : 0 ); + *ctype = (lclHeader.Flags & U_PPF_C ? 1 : 0 ); + *RgnID = (lclHeader.Flags >> U_FF_SHFT_OID8) & U_FF_MASK_OID8; U_PMF_SERIAL_get(&contents, BrushID, 4, 1, (*btype ? U_XE : U_LE)); /* color is not byte swapped, ID integer is */ return(1); } @@ -7556,6 +8042,8 @@ int U_PMR_FILLREGION_get(const char *contents, U_PMF_CMN_HDR *Header, EMF+ manual 2.3.5.1, Microsoft name: EmfPlusObject Record, Index 0x13 + Caller must check Data for possible memory access violations. + OTHER NOTES: All objects are to be stored in the same table and retrieved by index. Documentation indicates that this table contains only 64 slots, although the index @@ -7585,11 +8073,15 @@ int U_PMR_OBJECT_get(const char *contents, U_PMF_CMN_HDR *Header, uint32_t *ObjID, int *otype, int *ntype, uint32_t *TSize, const char **Data){ if(!contents || !ObjID || !otype || !ntype || !Data){ return(0); } - uint16_t Flags = U_PMF_HEADERFLAGS_get(contents); - *ntype = (Flags & U_PPF_N ? 1 : 0 ); - *ObjID = (Flags >> U_FF_SHFT_OID8) & U_FF_MASK_OID8; - *otype = (Flags >> U_FF_SHFT_OT) & U_FF_MASK_OT; - U_PMF_CMN_HDR_get(&contents, Header); + + U_PMF_CMN_HDR lclHeader; + U_PMF_CMN_HDR_get(&contents, &lclHeader); + if(lclHeader.Size < sizeof(U_PMF_OBJECT))return(0); + if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); } + + *ntype = (lclHeader.Flags & U_PPF_N ? 1 : 0 ); + *ObjID = (lclHeader.Flags >> U_FF_SHFT_OID8) & U_FF_MASK_OID8; + *otype = (lclHeader.Flags >> U_FF_SHFT_OT) & U_FF_MASK_OT; if(*ntype){ U_PMF_SERIAL_get(&contents, TSize, 4, 1, U_LE); } else { *TSize = 0; } U_PMF_PTRSAV_SHIFT(Data, &contents, 0); @@ -7606,12 +8098,19 @@ int U_PMR_OBJECT_get(const char *contents, U_PMF_CMN_HDR *Header, \param Data "Serialized image effects parameter block". One of the ImageEffects objects. EMF+ manual 2.3.5.2, Microsoft name: EmfPlusSerializableObject Record, Index 0x38 + + Caller must check Data for possible memory access violations. */ int U_PMR_SERIALIZABLEOBJECT_get(const char *contents, U_PMF_CMN_HDR *Header, uint8_t *GUID, uint32_t *Size, const char **Data){ if(!contents || !GUID || !Size || !Data){ return(0); } - U_PMF_CMN_HDR_get(&contents, Header); + + U_PMF_CMN_HDR lclHeader; + U_PMF_CMN_HDR_get(&contents, &lclHeader); + if(lclHeader.Size < sizeof(U_PMF_SERIALIZABLEOBJECT))return(0); + if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); } + U_PMF_SERIAL_get(&contents, GUID, 1, 16, U_XE); U_PMF_SERIAL_get(&contents, Size, 4, 1, U_LE); U_PMF_PTRSAV_SHIFT(Data, &contents, 0); @@ -7631,10 +8130,14 @@ int U_PMR_SERIALIZABLEOBJECT_get(const char *contents, U_PMF_CMN_HDR *Header, int U_PMR_SETANTIALIASMODE_get(const char *contents, U_PMF_CMN_HDR *Header, int *SMenum, int *aatype){ if(!contents || !SMenum || !aatype){ return(0); } - uint16_t Flags = U_PMF_HEADERFLAGS_get(contents); - *aatype = (Flags & U_PPF_AA ? 1 : 0 ); - *SMenum = (Flags >> U_FF_SHFT_AA) & U_FF_MASK_AA; - U_PMF_CMN_HDR_get(&contents, Header); + + U_PMF_CMN_HDR lclHeader; + U_PMF_CMN_HDR_get(&contents, &lclHeader); + if(lclHeader.Size < sizeof(U_PMF_SETANTIALIASMODE))return(0); + if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); } + + *aatype = (lclHeader.Flags & U_PPF_AA ? 1 : 0 ); + *SMenum = (lclHeader.Flags >> U_FF_SHFT_AA) & U_FF_MASK_AA; return(1); } @@ -7649,6 +8152,7 @@ int U_PMR_SETANTIALIASMODE_get(const char *contents, U_PMF_CMN_HDR *Header, */ int U_PMR_SETCOMPOSITINGMODE_get(const char *contents, U_PMF_CMN_HDR *Header, int *CMenum){ + /* memory access safe, only uses the common header */ if(!contents || !CMenum){ return(0); } uint16_t Flags = U_PMF_HEADERFLAGS_get(contents); *CMenum = (Flags >> U_FF_SHFT_CM) & U_FF_MASK_CM; @@ -7667,6 +8171,7 @@ int U_PMR_SETCOMPOSITINGMODE_get(const char *contents, U_PMF_CMN_HDR *Header, */ int U_PMR_SETCOMPOSITINGQUALITY_get(const char *contents, U_PMF_CMN_HDR *Header, int *CQenum){ + /* memory access safe, only uses the common header */ if(!contents || !CQenum){ return(0); } uint16_t Flags = U_PMF_HEADERFLAGS_get(contents); *CQenum = (Flags >> U_FF_SHFT_CQ) & U_FF_MASK_CQ; @@ -7685,6 +8190,7 @@ int U_PMR_SETCOMPOSITINGQUALITY_get(const char *contents, U_PMF_CMN_HDR *Header, */ int U_PMR_SETINTERPOLATIONMODE_get(const char *contents, U_PMF_CMN_HDR *Header, int *IMenum){ + /* memory access safe, only uses the common header */ if(!contents || !IMenum){ return(0); } uint16_t Flags = U_PMF_HEADERFLAGS_get(contents); *IMenum = (Flags >> U_FF_SHFT_IM) & U_FF_MASK_IM; @@ -7703,6 +8209,7 @@ int U_PMR_SETINTERPOLATIONMODE_get(const char *contents, U_PMF_CMN_HDR *Header, */ int U_PMR_SETPIXELOFFSETMODE_get(const char *contents, U_PMF_CMN_HDR *Header, int *POMenum){ + /* memory access safe, only uses the common header */ if(!contents || !POMenum){ return(0); } uint16_t Flags = U_PMF_HEADERFLAGS_get(contents); *POMenum = (Flags >> U_FF_SHFT_PxOffM) & U_FF_MASK_PxOffM; @@ -7723,7 +8230,12 @@ int U_PMR_SETPIXELOFFSETMODE_get(const char *contents, U_PMF_CMN_HDR *Header, int U_PMR_SETRENDERINGORIGIN_get(const char *contents, U_PMF_CMN_HDR *Header, int32_t *X, int32_t *Y){ if(!contents || !X || !Y){ return(0); } - U_PMF_CMN_HDR_get(&contents, Header); + + U_PMF_CMN_HDR lclHeader; + if(!U_PMF_CMN_HDR_get(&contents, &lclHeader))return(0); + if(lclHeader.Size < sizeof(U_PMF_SETRENDERINGORIGIN))return(0); + if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); } + U_PMF_SERIAL_get(&contents, X, 4, 1, U_LE); U_PMF_SERIAL_get(&contents, Y, 4, 1, U_LE); return(1); @@ -7740,6 +8252,7 @@ int U_PMR_SETRENDERINGORIGIN_get(const char *contents, U_PMF_CMN_HDR *Header, */ int U_PMR_SETTEXTCONTRAST_get(const char *contents, U_PMF_CMN_HDR *Header, int *TGC){ + /* memory access safe, only uses the common header */ if(!contents || !TGC){ return(0); } uint16_t Flags = U_PMF_HEADERFLAGS_get(contents); *TGC = (Flags >> U_FF_SHFT_TGC) & U_FF_MASK_TGC; @@ -7758,6 +8271,7 @@ int U_PMR_SETTEXTCONTRAST_get(const char *contents, U_PMF_CMN_HDR *Header, */ int U_PMR_SETTEXTRENDERINGHINT_get(const char *contents, U_PMF_CMN_HDR *Header, int *TRHenum){ + /* memory access safe, only uses the common header */ if(!contents || !TRHenum){ return(0); } uint16_t Flags = U_PMF_HEADERFLAGS_get(contents); *TRHenum = (Flags >> U_FF_SHFT_TRH) & U_FF_MASK_TRH; @@ -7781,9 +8295,13 @@ int U_PMR_BEGINCONTAINER_get(const char *contents, U_PMF_CMN_HDR *Header, int *UTenum, U_PMF_RECTF *DstRect, U_PMF_RECTF *SrcRect, uint32_t *StackID){ if(!contents || !UTenum || !DstRect || !SrcRect || !StackID){ return(0); } - uint16_t Flags = U_PMF_HEADERFLAGS_get(contents); - *UTenum = (Flags >> U_FF_SHFT_UT) & U_FF_MASK_UT; - U_PMF_CMN_HDR_get(&contents, Header); + + U_PMF_CMN_HDR lclHeader; + U_PMF_CMN_HDR_get(&contents, &lclHeader); + if(lclHeader.Size < sizeof(U_PMF_SETCLIPREGION))return(0); + if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); } + + *UTenum = (lclHeader.Flags >> U_FF_SHFT_UT) & U_FF_MASK_UT; U_PMF_SERIAL_get(&contents, DstRect, 4, 4, U_LE); U_PMF_SERIAL_get(&contents, SrcRect, 4, 4, U_LE); U_PMF_SERIAL_get(&contents, StackID, 4, 1, U_LE); @@ -7800,10 +8318,7 @@ int U_PMR_BEGINCONTAINER_get(const char *contents, U_PMF_CMN_HDR *Header, EMF+ manual 2.3.7.2, Microsoft name: EmfPlusBeginContainerNoParams Record, Index 0x28 */ int U_PMR_BEGINCONTAINERNOPARAMS_get(const char *contents, U_PMF_CMN_HDR *Header, uint32_t *StackID){ - if(!contents || !StackID){ return(0); } - U_PMF_CMN_HDR_get(&contents, Header); - U_PMF_SERIAL_get(&contents, StackID, 4, 1, U_LE); - return(1); + return(U_PMR_common_stack_get(contents, Header, StackID)); } /** @@ -7815,12 +8330,8 @@ int U_PMR_BEGINCONTAINERNOPARAMS_get(const char *contents, U_PMF_CMN_HDR *Header EMF+ manual 2.3.7.3, Microsoft name: EmfPlusEndContainer Record, Index 0x29 */ -int U_PMR_ENDCONTAINER_get(const char *contents, U_PMF_CMN_HDR *Header, - uint32_t *StackID){ - if(!contents || !StackID){ return(0); } - U_PMF_CMN_HDR_get(&contents, Header); - U_PMF_SERIAL_get(&contents, StackID, 4, 1, U_LE); - return(1); +int U_PMR_ENDCONTAINER_get(const char *contents, U_PMF_CMN_HDR *Header, uint32_t *StackID){ + return(U_PMR_common_stack_get(contents, Header, StackID)); } /** @@ -7833,10 +8344,7 @@ int U_PMR_ENDCONTAINER_get(const char *contents, U_PMF_CMN_HDR *Header, EMF+ manual 2.3.7.4, Microsoft name: EmfPlusRestore Record, Index 0x26 */ int U_PMR_RESTORE_get(const char *contents, U_PMF_CMN_HDR *Header, uint32_t *StackID){ - if(!contents || !StackID){ return(0); } - U_PMF_CMN_HDR_get(&contents, Header); - U_PMF_SERIAL_get(&contents, StackID, 4, 1, U_LE); - return(1); + return(U_PMR_common_stack_get(contents, Header, StackID)); } /** @@ -7849,10 +8357,7 @@ int U_PMR_RESTORE_get(const char *contents, U_PMF_CMN_HDR *Header, uint32_t *Sta EMF+ manual 2.3.7.5, Microsoft name: EmfPlusSave Record, Index 0x25 */ int U_PMR_SAVE_get(const char *contents, U_PMF_CMN_HDR *Header, uint32_t *StackID){ - if(!contents || !StackID){ return(0); } - U_PMF_CMN_HDR_get(&contents, Header); - U_PMF_SERIAL_get(&contents, StackID, 4, 1, U_LE); - return(1); + return(U_PMR_common_stack_get(contents, Header, StackID)); } /** @@ -7870,11 +8375,17 @@ int U_PMR_SETTSCLIP_get(const char *contents, U_PMF_CMN_HDR *Header, int *ctype, uint32_t *Elements, U_PMF_RECTF **Rects){ if(!contents || !ctype || !Elements || !Rects){ return(0); } - uint16_t Flags = U_PMF_HEADERFLAGS_get(contents); - *ctype = (Flags & U_PPF_K ? 1 : 0 ); - *Elements = (Flags >> U_FF_SHFT_TSC) & U_FF_MASK_TSC; - U_PMF_CMN_HDR_get(&contents, Header); - U_PMF_VARRECTS_get(&contents, Flags, *Elements, Rects); + + const char *blimit = contents; + U_PMF_CMN_HDR lclHeader; + U_PMF_CMN_HDR_get(&contents, &lclHeader); + if(lclHeader.Size < sizeof(U_PMF_SETTSCLIP))return(0); + if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); } + blimit += lclHeader.Size; + + *ctype = (lclHeader.Flags & U_PPF_K ? 1 : 0 ); + *Elements = (lclHeader.Flags >> U_FF_SHFT_TSC) & U_FF_MASK_TSC; + U_PMF_VARRECTS_get(&contents, lclHeader.Flags, *Elements, Rects, blimit); return(1); } @@ -7898,6 +8409,8 @@ int U_PMR_SETTSCLIP_get(const char *contents, U_PMF_CMN_HDR *Header, \param Data Palette (optional) EMF+ manual 2.3.8.2, Microsoft name: EmfPlusSetTSGraphics Record, Index 0x39 + + Caller must check Data for possible memory access violations. */ int U_PMR_SETTSGRAPHICS_get(const char *contents, U_PMF_CMN_HDR *Header, int *vgatype, int *pptype, @@ -7909,10 +8422,14 @@ int U_PMR_SETTSGRAPHICS_get(const char *contents, U_PMF_CMN_HDR *Header, !AntiAliasMode || !TextRenderHint || !CompositingMode || !CompositingQuality || !RenderOriginX || !RenderOriginY || !TextContrast || !FilterType || !PixelOffset || !WorldToDevice || !Data){ return(0); } - uint16_t Flags = U_PMF_HEADERFLAGS_get(contents); - *vgatype = (Flags & U_PPF_VGA ? 1 : 0 ); - *pptype = (Flags & U_PPF_PP ? 1 : 0 ); - U_PMF_CMN_HDR_get(&contents, Header); + + U_PMF_CMN_HDR lclHeader; + U_PMF_CMN_HDR_get(&contents, &lclHeader); + if(lclHeader.Size < sizeof(U_PMF_SETTSGRAPHICS))return(0); + if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); } + + *vgatype = (lclHeader.Flags & U_PPF_VGA ? 1 : 0 ); + *pptype = (lclHeader.Flags & U_PPF_PP ? 1 : 0 ); U_PMF_SERIAL_get(&contents, AntiAliasMode, 1, 1, U_XE); U_PMF_SERIAL_get(&contents, TextRenderHint, 1, 1, U_XE); U_PMF_SERIAL_get(&contents, CompositingMode, 1, 1, U_XE); @@ -7940,9 +8457,13 @@ int U_PMR_MULTIPLYWORLDTRANSFORM_get(const char *contents, U_PMF_CMN_HDR *Header int *xmtype, U_PMF_TRANSFORMMATRIX *Matrix){ if(!contents || !xmtype || !Matrix){ return(0); } - uint16_t Flags = U_PMF_HEADERFLAGS_get(contents); - *xmtype = (Flags & U_PPF_XM ? 1 : 0 ); - U_PMF_CMN_HDR_get(&contents, Header); + + U_PMF_CMN_HDR lclHeader; + U_PMF_CMN_HDR_get(&contents, &lclHeader); + if(lclHeader.Size < sizeof(U_PMF_MULTIPLYWORLDTRANSFORM))return(0); + if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); } + + *xmtype = (lclHeader.Flags & U_PPF_XM ? 1 : 0 ); U_PMF_SERIAL_get(&contents, Matrix, 4, 6, U_LE); return(1); } @@ -7956,9 +8477,7 @@ int U_PMR_MULTIPLYWORLDTRANSFORM_get(const char *contents, U_PMF_CMN_HDR *Header EMF+ manual 2.3.9.2, Microsoft name: EmfPlusResetWorldTransform Record, Index 0x2B */ int U_PMR_RESETWORLDTRANSFORM_get(const char *contents, U_PMF_CMN_HDR *Header){ - if(!contents){ return(0); } - U_PMF_CMN_HDR_get(&contents, Header); - return(1); + return( U_PMR_common_header_get(contents,Header)); } /** @@ -7975,9 +8494,13 @@ int U_PMR_ROTATEWORLDTRANSFORM_get(const char *contents, U_PMF_CMN_HDR *Header, int *xmtype, U_FLOAT *Angle){ if(!contents || !xmtype || !Angle){ return(0); } - uint16_t Flags = U_PMF_HEADERFLAGS_get(contents); - *xmtype = (Flags & U_PPF_XM ? 1 : 0 ); - U_PMF_CMN_HDR_get(&contents, Header); + + U_PMF_CMN_HDR lclHeader; + U_PMF_CMN_HDR_get(&contents, &lclHeader); + if(lclHeader.Size < sizeof(U_PMF_ROTATEWORLDTRANSFORM))return(0); + if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); } + + *xmtype = (lclHeader.Flags & U_PPF_XM ? 1 : 0 ); U_PMF_SERIAL_get(&contents, Angle, 4, 1, U_LE); return(1); } @@ -7997,9 +8520,13 @@ int U_PMR_SCALEWORLDTRANSFORM_get(const char *contents, U_PMF_CMN_HDR *Header, int *xmtype, U_FLOAT *Sx, U_FLOAT *Sy){ if(!contents || !xmtype || !Sx || !Sy){ return(0); } - uint16_t Flags = U_PMF_HEADERFLAGS_get(contents); - *xmtype = (Flags & U_PPF_XM ? 1 : 0 ); - U_PMF_CMN_HDR_get(&contents, Header); + + U_PMF_CMN_HDR lclHeader; + U_PMF_CMN_HDR_get(&contents, &lclHeader); + if(lclHeader.Size < sizeof(U_PMF_SCALEWORLDTRANSFORM))return(0); + if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); } + + *xmtype = (lclHeader.Flags & U_PPF_XM ? 1 : 0 ); U_PMF_SERIAL_get(&contents, Sx, 4, 1, U_LE); U_PMF_SERIAL_get(&contents, Sy, 4, 1, U_LE); return(1); @@ -8019,9 +8546,13 @@ int U_PMR_SETPAGETRANSFORM_get(const char *contents, U_PMF_CMN_HDR *Header, int *PUenum, U_FLOAT *Scale){ if(!contents || !PUenum || !Scale){ return(0); } - uint16_t Flags = U_PMF_HEADERFLAGS_get(contents); - *PUenum = (Flags >> U_FF_SHFT_PU) & U_FF_MASK_PU; - U_PMF_CMN_HDR_get(&contents, Header); + + U_PMF_CMN_HDR lclHeader; + U_PMF_CMN_HDR_get(&contents, &lclHeader); + if(lclHeader.Size < sizeof(U_PMF_SETPAGETRANSFORM))return(0); + if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); } + + *PUenum = (lclHeader.Flags >> U_FF_SHFT_PU) & U_FF_MASK_PU; U_PMF_SERIAL_get(&contents, Scale, 4, 1, U_LE); return(1); } @@ -8038,7 +8569,12 @@ int U_PMR_SETPAGETRANSFORM_get(const char *contents, U_PMF_CMN_HDR *Header, int U_PMR_SETWORLDTRANSFORM_get(const char *contents, U_PMF_CMN_HDR *Header, U_PMF_TRANSFORMMATRIX *Matrix){ if(!contents || !Matrix){ return(0); } - U_PMF_CMN_HDR_get(&contents, Header); + + U_PMF_CMN_HDR lclHeader; + U_PMF_CMN_HDR_get(&contents, &lclHeader); + if(lclHeader.Size < sizeof(U_PMF_SETWORLDTRANSFORM))return(0); + if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); } + U_PMF_SERIAL_get(&contents, Matrix, 4, 6, U_LE); return(1); } @@ -8058,9 +8594,13 @@ int U_PMR_TRANSLATEWORLDTRANSFORM_get(const char *contents, U_PMF_CMN_HDR *Heade int *xmtype, U_FLOAT *Dx, U_FLOAT *Dy){ if(!contents || !xmtype || !Dx || !Dy){ return(0); } - uint16_t Flags = U_PMF_HEADERFLAGS_get(contents); - *xmtype = (Flags & U_PPF_XM ? 1 : 0 ); - U_PMF_CMN_HDR_get(&contents, Header); + + U_PMF_CMN_HDR lclHeader; + U_PMF_CMN_HDR_get(&contents, &lclHeader); + if(lclHeader.Size < sizeof(U_PMF_TRANSLATEWORLDTRANSFORM))return(0); + if(Header){ memcpy(Header,&lclHeader,sizeof(U_PMF_CMN_HDR)); } + + *xmtype = (lclHeader.Flags & U_PPF_XM ? 1 : 0 ); U_PMF_SERIAL_get(&contents, Dx, 4, 1, U_LE); U_PMF_SERIAL_get(&contents, Dy, 4, 1, U_LE); return(1); @@ -8079,9 +8619,7 @@ int U_PMR_TRANSLATEWORLDTRANSFORM_get(const char *contents, U_PMF_CMN_HDR *Heade the path by using the current pen, and fills its interior by using the current brush." */ int U_PMR_STROKEFILLPATH_get(const char *contents, U_PMF_CMN_HDR *Header){ - if(!contents){ return(0); } - U_PMF_CMN_HDR_get(&contents, Header); - return(1); + return( U_PMR_common_header_get(contents,Header)); } /** @@ -8093,9 +8631,7 @@ int U_PMR_STROKEFILLPATH_get(const char *contents, U_PMF_CMN_HDR *Header){ EMF+ manual mentioned in 2.1.1.1, reserved, not otherwise documented, Microsoft name: EmfPlusMultiFormatStart Record, Index 0x05 */ int U_PMR_MULTIFORMATSTART_get(const char *contents, U_PMF_CMN_HDR *Header){ - if(!contents){ return(0); } - U_PMF_CMN_HDR_get(&contents, Header); - return(1); + return( U_PMR_common_header_get(contents,Header)); } /** @@ -8107,9 +8643,7 @@ int U_PMR_MULTIFORMATSTART_get(const char *contents, U_PMF_CMN_HDR *Header){ EMF+ manual mentioned in 2.1.1.1, reserved, not otherwise documented, Microsoft name: EmfPlusMultiFormatSection Record, Index 0x06 */ int U_PMR_MULTIFORMATSECTION_get(const char *contents, U_PMF_CMN_HDR *Header){ - if(!contents){ return(0); } - U_PMF_CMN_HDR_get(&contents, Header); - return(1); + return( U_PMR_common_header_get(contents,Header)); } /** @@ -8121,9 +8655,7 @@ int U_PMR_MULTIFORMATSECTION_get(const char *contents, U_PMF_CMN_HDR *Header){ EMF+ manual mentioned in 2.1.1.1, reserved, not otherwise documented, Microsoft name: EmfPlusMultiFormatEnd Record, Index 0x06 */ int U_PMR_MULTIFORMATEND_get(const char *contents, U_PMF_CMN_HDR *Header){ - if(!contents){ return(0); } - U_PMF_CMN_HDR_get(&contents, Header); - return(1); + return( U_PMR_common_header_get(contents,Header)); } diff --git a/src/libuemf/upmf.h b/src/libuemf/upmf.h index 27ac94a89..dbda02ba7 100644 --- a/src/libuemf/upmf.h +++ b/src/libuemf/upmf.h @@ -27,11 +27,11 @@ /* File: upmf.h -Version: 0.0.2 -Date: 04-NOV-2013 +Version: 0.0.4 +Date: 17-MAR-2015 Author: David Mathog, Biology Division, Caltech email: mathog@caltech.edu -Copyright: 2013 David Mathog and California Institute of Technology (Caltech) +Copyright: 2015 David Mathog and California Institute of Technology (Caltech) */ #ifndef _UPMF_ @@ -1805,9 +1805,8 @@ typedef struct { /** @brief EMF+ manual 2.2.2.25, Microsoft name: EmfPlusLinearGradientBrushOptionalData Object For U_PMF_LINEARGRADIENTBRUSHDATA data field */ -typedef struct { - U_PMF_ROTMATRIX Matrix; //!< Rotation matrix, Manuals says that this should be Transformation matrix, but last two values are missing -/* variable part of object, not part of structure. +/* Entire object is variable and not part of a structure! U_PMF_LINEARGRADIENTBRUSHOPTIONALDATA + U_PMF_ROTMATRIX Matrix; //!< Rotation matrix, Manuals says that this should be Transformation matrix, but last two values are missing (various) pattern; //!< Presence and meaning depend on Flags field, see below Flag values @@ -1818,7 +1817,6 @@ typedef struct { 0 0 1 U_PMF_BLENDFACTORS 0 1 1 U_PMF_BLENDFACTORS, U_PMF_BLENDFACTORS */ -} U_PMF_LINEARGRADIENTBRUSHOPTIONALDATA; /** @brief EMF+ manual 2.2.2.26, Microsoft name: EmfPlusLinePath Object */ typedef struct { @@ -1888,7 +1886,10 @@ typedef struct { Bitfields in the FILE (LITTLE endian here, manual uses BIG endian) are: bits 4-7 PathPointType flags bits 0-3 PathPointType enumeration + @{ */ +typedef uint8_t U_PMF_PATHPOINTTYPE; //!< EMF+ manual 2.2.2.31, Microsoft name: EmfPlusPathPointType Object +/** @} */ /** \defgroup U_PMF_PPTYPERLE PMF Run Length Encoded Path Point Types @brief EMF+ manual 2.2.2.32, Microsoft name: EmfPlusPathPointTypeRLE Object @@ -2838,6 +2839,7 @@ void U_PMF_MEMCPY_DSTSHIFT(char **Dst, const void *Src, size_t Size); void U_PMF_REPCPY_DSTSHIFT(char **Dst, const void *Src, size_t Size, size_t Reps); void U_PMF_PTRSAV_SHIFT(const char **Dst, const char **Src, size_t Size); uint16_t U_PMF_HEADERFLAGS_get(const char *contents); +int U_PMF_RECORD_SIZE_get(const char *contents); int U_PMF_CMN_HDR_get(const char **contents, U_PMF_CMN_HDR *Header); int U_OID_To_OT(uint32_t OID); int U_OID_To_BT(uint32_t OID); @@ -3035,77 +3037,77 @@ U_PSEUDO_OBJ *U_PMR_STROKEFILLPATH_set(void); /* EMF+ prototypes (objects_get) */ -int U_PMF_BRUSH_get(const char *contents, uint32_t *Version, uint32_t *Type, const char **Data); -int U_PMF_CUSTOMLINECAP_get(const char *contents, uint32_t *Version, uint32_t *Type, const char **Data); -int U_PMF_FONT_get(const char *contents, uint32_t *Version, U_FLOAT *EmSize, uint32_t *SizeUnit, int32_t *FSFlags, uint32_t *Length, const char **Data); -int U_PMF_IMAGE_get(const char *contents, uint32_t *Version, uint32_t *Type, const char **Data); -int U_PMF_IMAGEATTRIBUTES_get(const char *contents, uint32_t *Version, uint32_t *WrapMode, uint32_t *ClampColor, uint32_t *ObjectClamp); -int U_PMF_PATH_get(const char *contents, uint32_t *Version, uint32_t *Count, uint16_t *Flags, const char **Points, const char **Types); -int U_PMF_PEN_get(const char *contents, uint32_t *Version, uint32_t *Type, const char **PenData, const char **Brush); -int U_PMF_REGION_get(const char *contents, uint32_t *Version, uint32_t *Count, const char **Nodes); -int U_PMF_STRINGFORMAT_get(const char *contents, U_PMF_STRINGFORMAT *Sfs, const char **Data); -int U_PMF_ARGB_get(const char *contents, uint8_t *Blue, uint8_t *Green, uint8_t *Red, uint8_t *Alpha); -int U_PMF_BITMAP_get(const char *contents, U_PMF_BITMAP *Bs, const char **Data); -int U_PMF_BITMAPDATA_get(const char *contents, U_PMF_PALETTE *Ps, const char **Colors, const char **Data); -int U_PMF_BLENDCOLORS_get(const char *contents, uint32_t *Elements, U_FLOAT **Positions, const char **Colors); -int U_PMF_BLENDFACTORS_get(const char *contents, uint32_t *Elements, U_FLOAT **Positions, U_FLOAT **Factors); -int U_PMF_BOUNDARYPATHDATA_get(const char *contents, int32_t *Size, const char **Data); -int U_PMF_BOUNDARYPOINTDATA_get(const char *contents, int32_t *Elements, U_PMF_POINTF **Points); -int U_PMF_CHARACTERRANGE_get(const char *contents, int32_t *First, int32_t *Length); -int U_PMF_COMPOUNDLINEDATA_get(const char *contents, int32_t *Elements, U_FLOAT **Widths); -int U_PMF_COMPRESSEDIMAGE_get(const char *contents, const char **Data); -int U_PMF_CUSTOMENDCAPDATA_get(const char *contents, int32_t *Size, const char **Data); -int U_PMF_CUSTOMLINECAPARROWDATA_get(const char *contents, U_PMF_CUSTOMLINECAPARROWDATA *Ccad); -int U_PMF_CUSTOMLINECAPDATA_get(const char *contents, U_PMF_CUSTOMLINECAPDATA *Clcd, const char **Data); -int U_PMF_CUSTOMLINECAPOPTIONALDATA_get(const char *contents, uint32_t Flags, const char **FillData, const char **LineData); -int U_PMF_CUSTOMSTARTCAPDATA_get(const char *contents, int32_t *Size, const char **Data); -int U_PMF_DASHEDLINEDATA_get(const char *contents, int32_t *Elements, U_FLOAT **Lengths); -int U_PMF_FILLPATHOBJ_get(const char *contents, int32_t *Size, const char **Data); -int U_PMF_FOCUSSCALEDATA_get(const char *contents, uint32_t *Count, U_FLOAT *ScaleX, U_FLOAT *ScaleY); -int U_PMF_GRAPHICSVERSION_get(const char *contents, int *Signature, int *GrfVersion); -int U_PMF_HATCHBRUSHDATA_get(const char *contents, uint32_t *Style, U_PMF_ARGB *Foreground, U_PMF_ARGB *Background); -int U_PMF_INTEGER7_get(const char **contents, U_FLOAT *Value); -int U_PMF_INTEGER15_get(const char **contents, U_FLOAT *Value); +int U_PMF_BRUSH_get(const char *contents, uint32_t *Version, uint32_t *Type, const char **Data, const char *blimit); +int U_PMF_CUSTOMLINECAP_get(const char *contents, uint32_t *Version, uint32_t *Type, const char **Data, const char *blimit); +int U_PMF_FONT_get(const char *contents, uint32_t *Version, U_FLOAT *EmSize, uint32_t *SizeUnit, int32_t *FSFlags, uint32_t *Length, const char **Data, const char *blimit); +int U_PMF_IMAGE_get(const char *contents, uint32_t *Version, uint32_t *Type, const char **Data, const char *blimit); +int U_PMF_IMAGEATTRIBUTES_get(const char *contents, uint32_t *Version, uint32_t *WrapMode, uint32_t *ClampColor, uint32_t *ObjectClamp, const char *blimit); +int U_PMF_PATH_get(const char *contents, uint32_t *Version, uint32_t *Count, uint16_t *Flags, const char **Points, const char **Types, const char *blimit); +int U_PMF_PEN_get(const char *contents, uint32_t *Version, uint32_t *Type, const char **PenData, const char **Brush, const char *blimit); +int U_PMF_REGION_get(const char *contents, uint32_t *Version, uint32_t *Count, const char **Nodes, const char *blimit); +int U_PMF_STRINGFORMAT_get(const char *contents, U_PMF_STRINGFORMAT *Sfs, const char **Data, const char *blimit); +int U_PMF_ARGB_get(const char *contents, uint8_t *Blue, uint8_t *Green, uint8_t *Red, uint8_t *Alpha, const char *blimit); +int U_PMF_BITMAP_get(const char *contents, U_PMF_BITMAP *Bs, const char **Data, const char *blimit); +int U_PMF_BITMAPDATA_get(const char *contents, U_PMF_PALETTE *Ps, const char **Colors, const char **Data, const char *blimit); +int U_PMF_BLENDCOLORS_get(const char *contents, uint32_t *Elements, U_FLOAT **Positions, const char **Colors, const char *blimit); +int U_PMF_BLENDFACTORS_get(const char *contents, uint32_t *Elements, U_FLOAT **Positions, U_FLOAT **Factors, const char *blimit); +int U_PMF_BOUNDARYPATHDATA_get(const char *contents, int32_t *Size, const char **Data, const char *blimit); +int U_PMF_BOUNDARYPOINTDATA_get(const char *contents, int32_t *Elements, U_PMF_POINTF **Points, const char *blimit); +int U_PMF_CHARACTERRANGE_get(const char *contents, int32_t *First, int32_t *Length, const char *blimit); +int U_PMF_COMPOUNDLINEDATA_get(const char *contents, int32_t *Elements, U_FLOAT **Widths, const char *blimit); +int U_PMF_COMPRESSEDIMAGE_get(const char *contents, const char **Data, const char *blimit); +int U_PMF_CUSTOMENDCAPDATA_get(const char *contents, int32_t *Size, const char **Data, const char *blimit); +int U_PMF_CUSTOMLINECAPARROWDATA_get(const char *contents, U_PMF_CUSTOMLINECAPARROWDATA *Ccad, const char *blimit); +int U_PMF_CUSTOMLINECAPDATA_get(const char *contents, U_PMF_CUSTOMLINECAPDATA *Clcd, const char **Data, const char *blimit); +int U_PMF_CUSTOMLINECAPOPTIONALDATA_get(const char *contents, uint32_t Flags, const char **FillData, const char **LineData, const char *blimit); +int U_PMF_CUSTOMSTARTCAPDATA_get(const char *contents, int32_t *Size, const char **Data, const char *blimit); +int U_PMF_DASHEDLINEDATA_get(const char *contents, int32_t *Elements, U_FLOAT **Lengths, const char *blimit); +int U_PMF_FILLPATHOBJ_get(const char *contents, int32_t *Size, const char **Data, const char *blimit); +int U_PMF_FOCUSSCALEDATA_get(const char *contents, uint32_t *Count, U_FLOAT *ScaleX, U_FLOAT *ScaleY, const char *blimit); +int U_PMF_GRAPHICSVERSION_get(const char *contents, int *Signature, int *GrfVersion, const char *blimit); +int U_PMF_HATCHBRUSHDATA_get(const char *contents, uint32_t *Style, U_PMF_ARGB *Foreground, U_PMF_ARGB *Background, const char *blimit); +int U_PMF_INTEGER7_get(const char **contents, U_FLOAT *Value, const char *blimit); +int U_PMF_INTEGER15_get(const char **contents, U_FLOAT *Value, const char *blimit); int U_PMF_LANGUAGEIDENTIFIER_get(U_PMF_LANGUAGEIDENTIFIER LId, int *SubLId, int *PriLId); -int U_PMF_LINEARGRADIENTBRUSHDATA_get(const char *contents, U_PMF_LINEARGRADIENTBRUSHDATA *Lgbd, const char **Data); -int U_PMF_LINEARGRADIENTBRUSHOPTIONALDATA_get(const char *contents, uint32_t Flags, U_PMF_TRANSFORMMATRIX *Tm, const char **Bc, const char **BfH, const char **BfV); -int U_PMF_LINEPATH_get(const char *contents, int32_t *Size, const char **Data); -int U_PMF_METAFILE_get(const char *contents, uint32_t *Type, uint32_t *Size, const char **Data); -int U_PMF_PALETTE_get(const char *contents, uint32_t *Flags, uint32_t *Elements, const char **Data); -int U_PMF_PATHGRADIENTBRUSHDATA_get(const char *contents, U_PMF_PATHGRADIENTBRUSHDATA *Pgbd, const char **Gradient, const char **Boundary, const char **Data); -int U_PMF_PATHGRADIENTBRUSHOPTIONALDATA_get(const char *contents, uint32_t Flags, U_PMF_TRANSFORMMATRIX *Matrix, const char **Pattern, const char **Data); -int U_PMF_PATHPOINTTYPE_get(const char *contents, int *Flags, int *Type); -int U_PMF_PATHPOINTTYPERLE_get(const char *contents, int *Bezier, int *RL, int *Ppt); -int U_PMF_PENDATA_get(const char *contents, uint32_t *Flags, uint32_t *Unit, U_FLOAT *Width, const char **Data); +int U_PMF_LINEARGRADIENTBRUSHDATA_get(const char *contents, U_PMF_LINEARGRADIENTBRUSHDATA *Lgbd, const char **Data, const char *blimit); +int U_PMF_LINEARGRADIENTBRUSHOPTIONALDATA_get(const char *contents, uint32_t Flags, U_PMF_TRANSFORMMATRIX *Tm, const char **Bc, const char **BfH, const char **BfV, const char *blimit); +int U_PMF_LINEPATH_get(const char *contents, int32_t *Size, const char **Data, const char *blimit); +int U_PMF_METAFILE_get(const char *contents, uint32_t *Type, uint32_t *Size, const char **Data, const char *blimit); +int U_PMF_PALETTE_get(const char *contents, uint32_t *Flags, uint32_t *Elements, const char **Data, const char *blimit); +int U_PMF_PATHGRADIENTBRUSHDATA_get(const char *contents, U_PMF_PATHGRADIENTBRUSHDATA *Pgbd, const char **Gradient, const char **Boundary, const char **Data, const char *blimit); +int U_PMF_PATHGRADIENTBRUSHOPTIONALDATA_get(const char *contents, uint32_t Flags, U_PMF_TRANSFORMMATRIX *Matrix, const char **Pattern, const char **Data, const char *blimit); +int U_PMF_PATHPOINTTYPE_get(const char *contents, int *Flags, int *Type, const char *blimit); +int U_PMF_PATHPOINTTYPERLE_get(const char *contents, int *Bezier, int *RL, int *Ppt, const char *blimit); +int U_PMF_PENDATA_get(const char *contents, uint32_t *Flags, uint32_t *Unit, U_FLOAT *Width, const char **Data, const char *blimit); int U_PMF_PENOPTIONALDATA_get(const char *contents, uint32_t Flags, U_PMF_TRANSFORMMATRIX *Matrix, int32_t *StartCap, int32_t *EndCap, uint32_t *Join, U_FLOAT *MiterLimit, int32_t *Style, int32_t *DLCap, U_FLOAT *DLOffset, - const char **DLData, int32_t *Alignment, const char **CmpndLineData, const char **CSCapData, const char **CECapData); -int U_PMF_POINT_get(const char **contents, U_FLOAT *X, U_FLOAT *Y); -int U_PMF_POINTF_get(const char **contents, U_FLOAT *X, U_FLOAT *Y); -int U_PMF_POINTR_get(const char **contents, U_FLOAT *X, U_FLOAT *Y); -int U_PMF_RECT_get(const char **contents, int16_t *X, int16_t *Y, int16_t *Width, int16_t *Height); -int U_PMF_RECTF_get(const char **contents, U_FLOAT *X, U_FLOAT *Y, U_FLOAT *Width, U_FLOAT *Height); -int U_PMF_REGIONNODE_get(const char *contents, uint32_t *Type, const char **Data); + const char **DLData, int32_t *Alignment, const char **CmpndLineData, const char **CSCapData, const char **CECapData, const char *blimit); +int U_PMF_POINT_get(const char **contents, U_FLOAT *X, U_FLOAT *Y, const char *blimit); +int U_PMF_POINTF_get(const char **contents, U_FLOAT *X, U_FLOAT *Y, const char *blimit); +int U_PMF_POINTR_get(const char **contents, U_FLOAT *X, U_FLOAT *Y, const char *blimit); +int U_PMF_RECT_get(const char **contents, int16_t *X, int16_t *Y, int16_t *Width, int16_t *Height, const char *blimit); +int U_PMF_RECTF_get(const char **contents, U_FLOAT *X, U_FLOAT *Y, U_FLOAT *Width, U_FLOAT *Height, const char *blimit); +int U_PMF_REGIONNODE_get(const char *contents, uint32_t *Type, const char **Data, const char *blimit); /* There is no U_PMF_REGIONNODECHILDNODES_get, see the note in upmf.c */ -int U_PMF_REGIONNODEPATH_get(const char *contents, int32_t *Size, const char **Data); -int U_PMF_SOLIDBRUSHDATA_get(const char *contents, U_PMF_ARGB *Color); +int U_PMF_REGIONNODEPATH_get(const char *contents, int32_t *Size, const char **Data, const char *blimit); +int U_PMF_SOLIDBRUSHDATA_get(const char *contents, U_PMF_ARGB *Color, const char *blimit); int U_PMF_STRINGFORMATDATA_get(const char *contents, uint32_t TabStopCount, uint32_t RangeCount, - const U_FLOAT **TabStops, const U_PMF_CHARACTERRANGE **CharRange); -int U_PMF_TEXTUREBRUSHDATA_get(const char *contents, uint32_t *Flags, int32_t *WrapMode, const char **Data); -int U_PMF_TEXTUREBRUSHOPTIONALDATA_get(const char *contents, int HasImage, U_PMF_TRANSFORMMATRIX *Matrix, const char **Image); -int U_PMF_TRANSFORMMATRIX_get(const char *contents, U_PMF_TRANSFORMMATRIX *Matrix); -int U_PMF_IE_BLUR_get(const char *contents, U_FLOAT *Radius, uint32_t *ExpandEdge); -int U_PMF_IE_BRIGHTNESSCONTRAST_get(const char *contents, int32_t *Brightness, int32_t *Contrast); -int U_PMF_IE_COLORBALANCE_get(const char *contents, int32_t *CyanRed, int32_t *MagentaGreen, int32_t *YellowBlue); -int U_PMF_IE_COLORCURVE_get(const char *contents, uint32_t *Adjust, uint32_t *Channel, int32_t *Intensity); + const U_FLOAT **TabStops, const U_PMF_CHARACTERRANGE **CharRange, const char *blimit); +int U_PMF_TEXTUREBRUSHDATA_get(const char *contents, uint32_t *Flags, int32_t *WrapMode, const char **Data, const char *blimit); +int U_PMF_TEXTUREBRUSHOPTIONALDATA_get(const char *contents, int HasImage, U_PMF_TRANSFORMMATRIX *Matrix, const char **Image, const char *blimit); +int U_PMF_TRANSFORMMATRIX_get(const char *contents, U_PMF_TRANSFORMMATRIX *Matrix, const char *blimit); +int U_PMF_IE_BLUR_get(const char *contents, U_FLOAT *Radius, uint32_t *ExpandEdge, const char *blimit); +int U_PMF_IE_BRIGHTNESSCONTRAST_get(const char *contents, int32_t *Brightness, int32_t *Contrast, const char *blimit); +int U_PMF_IE_COLORBALANCE_get(const char *contents, int32_t *CyanRed, int32_t *MagentaGreen, int32_t *YellowBlue, const char *blimit); +int U_PMF_IE_COLORCURVE_get(const char *contents, uint32_t *Adjust, uint32_t *Channel, int32_t *Intensity, const char *blimit); int U_PMF_IE_COLORLOOKUPTABLE_get(const char *contents, - const uint8_t **BLUT, const uint8_t **GLUT, const uint8_t **RLUT, const uint8_t **ALUT); -int U_PMF_IE_COLORMATRIX_get(const char *contents, U_PMF_IE_COLORMATRIX *Matrix); -int U_PMF_IE_HUESATURATIONLIGHTNESS_get(const char *contents, int32_t *Hue, int32_t *Saturation, int32_t *Lightness); -int U_PMF_IE_LEVELS_get(const char *contents, int32_t *Highlight, int32_t *Midtone, int32_t *Shadow); -int U_PMF_IE_REDEYECORRECTION_get(const char *contents, int32_t *Elements, U_RECTL **Rects); -int U_PMF_IE_SHARPEN_get(const char *contents, U_FLOAT *Radius, int32_t *Sharpen); -int U_PMF_IE_TINT_get(const char *contents, int32_t *Hue, int32_t *Amount); + const uint8_t **BLUT, const uint8_t **GLUT, const uint8_t **RLUT, const uint8_t **ALUT, const char *blimit); +int U_PMF_IE_COLORMATRIX_get(const char *contents, U_PMF_IE_COLORMATRIX *Matrix, const char *blimit); +int U_PMF_IE_HUESATURATIONLIGHTNESS_get(const char *contents, int32_t *Hue, int32_t *Saturation, int32_t *Lightness, const char *blimit); +int U_PMF_IE_LEVELS_get(const char *contents, int32_t *Highlight, int32_t *Midtone, int32_t *Shadow, const char *blimit); +int U_PMF_IE_REDEYECORRECTION_get(const char *contents, int32_t *Elements, U_RECTL **Rects, const char *blimit); +int U_PMF_IE_SHARPEN_get(const char *contents, U_FLOAT *Radius, int32_t *Sharpen, const char *blimit); +int U_PMF_IE_TINT_get(const char *contents, int32_t *Hue, int32_t *Amount, const char *blimit); /* EMF+ prototypes (records_get) */ diff --git a/src/libuemf/upmf_print.c b/src/libuemf/upmf_print.c index 13bbfade8..58ff4edd0 100644 --- a/src/libuemf/upmf_print.c +++ b/src/libuemf/upmf_print.c @@ -6,11 +6,11 @@ /* File: upmf_print.c -Version: 0.0.3 -Date: 24-MAR-2014 +Version: 0.0.7 +Date: 21-MAY-2015 Author: David Mathog, Biology Division, Caltech email: mathog@caltech.edu -Copyright: 2014 David Mathog and California Institute of Technology (Caltech) +Copyright: 2015 David Mathog and California Institute of Technology (Caltech) */ /* compiler options: @@ -30,6 +30,7 @@ extern "C" { #include <string.h> #include "upmf_print.h" #include "uemf_print.h" +#include "uemf_safe.h" //! \cond @@ -49,7 +50,7 @@ int U_PMR_NODATAREC_print(const char *contents){ /* this function is not visible in the API. Common routine used by many functions that draw points. */ -void U_PMF_VARPOINTS_print(const char **contents, int Flags, uint32_t Elements){ +void U_PMF_VARPOINTS_print(const char **contents, int Flags, uint32_t Elements, const char *blimit){ unsigned int i; U_FLOAT Xpos, Ypos; @@ -58,9 +59,9 @@ void U_PMF_VARPOINTS_print(const char **contents, int Flags, uint32_t Elements){ else { printf(" + Points(Float):"); } for(Xpos = Ypos = i = 0; i<Elements; i++){ printf(" %d:",i); - if( Flags & U_PPF_P){ (void) U_PMF_POINTR_print(contents, &Xpos, &Ypos); } - else if(Flags & U_PPF_C){ (void) U_PMF_POINT_print(contents); } - else { (void) U_PMF_POINTF_print(contents); } + if( Flags & U_PPF_P){ (void) U_PMF_POINTR_print(contents, &Xpos, &Ypos, blimit); } + else if(Flags & U_PPF_C){ (void) U_PMF_POINT_print(contents, blimit); } + else { (void) U_PMF_POINTF_print(contents, blimit); } } #if 0 int residual; @@ -120,21 +121,28 @@ int U_PMF_VARBRUSHID_print(int btype, uint32_t BrushID){ \brief Print any EMF+ record \returns record length for a normal record, 0 for EMREOF or , -1 for a bad record \param contents pointer to a buffer holding this EMF+ record - \param blimit one byte past the end of data of this EMF+ record + \param blimit one byte after this EMF+ record \param recnum EMF number of this record in contents - \param off Offset from the beginning of the EMF+ file. + \param off Offset from the beginning of the EMF+ file to the start of this record. */ int U_pmf_onerec_print(const char *contents, const char *blimit, int recnum, int off){ int status; + int rstatus; static U_OBJ_ACCUM ObjCont={NULL,0,0,0,0}; /* for keeping track of object continuation. These may be split across multiple EMF Comment records */ U_PMF_CMN_HDR Header; const char *contemp = contents; + + /* Check that COMMON header data in record can be touched without an access violation. If it cannot be + this is either a corrupt EMF or one engineered to cause a buffer overflow. Pointer math + could wrap so check both sides of the range. + */ + if(IS_MEM_UNSAFE(contents, sizeof(U_PMF_CMN_HDR), blimit))return(-1); if(!U_PMF_CMN_HDR_get(&contemp, &Header)){return(-1);} int type = Header.Type & U_PMR_TYPE_MASK; /* strip the U_PMR_RECFLAG bit, leaving the indexable part */ if(type < U_PMR_MIN || type > U_PMR_MAX)return(-1); /* unknown EMF+ record type */ - status = U_PMF_CMN_HDR_print(Header, recnum, off); /* EMF+ part */ + status = U_PMF_CMN_HDR_print(contents, Header, recnum, off); /* EMF+ part */ /* Buggy EMF+ can set the continue bit and then do something else. In that case, force out the pending Object. Side effect - clears the pending object. */ @@ -142,81 +150,88 @@ int U_pmf_onerec_print(const char *contents, const char *blimit, int recnum, int U_PMR_OBJECT_print(contents, blimit, &ObjCont, 1); } + /* Check that the record size is OK, abort if not. */ + if(Header.Size < sizeof(U_PMF_CMN_HDR) || + IS_MEM_UNSAFE(contents, Header.Size, blimit))return(-1); + switch(type){ - case (U_PMR_HEADER): U_PMR_HEADER_print(contents); break; - case (U_PMR_ENDOFFILE): U_PMR_ENDOFFILE_print(contents); - U_OA_release(&ObjCont); break; - case (U_PMR_COMMENT): U_PMR_COMMENT_print(contents); break; - case (U_PMR_GETDC): U_PMR_GETDC_print(contents); break; - case (U_PMR_MULTIFORMATSTART): U_PMR_MULTIFORMATSTART_print(contents); break; - case (U_PMR_MULTIFORMATSECTION): U_PMR_MULTIFORMATSECTION_print(contents); break; - case (U_PMR_MULTIFORMATEND): U_PMR_MULTIFORMATEND_print(contents); break; - case (U_PMR_OBJECT): U_PMR_OBJECT_print(contents,blimit,&ObjCont,0); break; - case (U_PMR_CLEAR): U_PMR_CLEAR_print(contents); break; - case (U_PMR_FILLRECTS): U_PMR_FILLRECTS_print(contents, blimit); break; - case (U_PMR_DRAWRECTS): U_PMR_DRAWRECTS_print(contents, blimit); break; - case (U_PMR_FILLPOLYGON): U_PMR_FILLPOLYGON_print(contents); break; - case (U_PMR_DRAWLINES): U_PMR_DRAWLINES_print(contents); break; - case (U_PMR_FILLELLIPSE): U_PMR_FILLELLIPSE_print(contents); break; - case (U_PMR_DRAWELLIPSE): U_PMR_DRAWELLIPSE_print(contents); break; - case (U_PMR_FILLPIE): U_PMR_FILLPIE_print(contents); break; - case (U_PMR_DRAWPIE): U_PMR_DRAWPIE_print(contents); break; - case (U_PMR_DRAWARC): U_PMR_DRAWARC_print(contents); break; - case (U_PMR_FILLREGION): U_PMR_FILLREGION_print(contents); break; - case (U_PMR_FILLPATH): U_PMR_FILLPATH_print(contents); break; - case (U_PMR_DRAWPATH): U_PMR_DRAWPATH_print(contents); break; - case (U_PMR_FILLCLOSEDCURVE): U_PMR_FILLCLOSEDCURVE_print(contents); break; - case (U_PMR_DRAWCLOSEDCURVE): U_PMR_DRAWCLOSEDCURVE_print(contents); break; - case (U_PMR_DRAWCURVE): U_PMR_DRAWCURVE_print(contents); break; - case (U_PMR_DRAWBEZIERS): U_PMR_DRAWBEZIERS_print(contents); break; - case (U_PMR_DRAWIMAGE): U_PMR_DRAWIMAGE_print(contents); break; - case (U_PMR_DRAWIMAGEPOINTS): U_PMR_DRAWIMAGEPOINTS_print(contents); break; - case (U_PMR_DRAWSTRING): U_PMR_DRAWSTRING_print(contents); break; - case (U_PMR_SETRENDERINGORIGIN): U_PMR_SETRENDERINGORIGIN_print(contents); break; - case (U_PMR_SETANTIALIASMODE): U_PMR_SETANTIALIASMODE_print(contents); break; - case (U_PMR_SETTEXTRENDERINGHINT): U_PMR_SETTEXTRENDERINGHINT_print(contents); break; - case (U_PMR_SETTEXTCONTRAST): U_PMR_SETTEXTCONTRAST_print(contents); break; - case (U_PMR_SETINTERPOLATIONMODE): U_PMR_SETINTERPOLATIONMODE_print(contents); break; - case (U_PMR_SETPIXELOFFSETMODE): U_PMR_SETPIXELOFFSETMODE_print(contents); break; - case (U_PMR_SETCOMPOSITINGMODE): U_PMR_SETCOMPOSITINGMODE_print(contents); break; - case (U_PMR_SETCOMPOSITINGQUALITY): U_PMR_SETCOMPOSITINGQUALITY_print(contents); break; - case (U_PMR_SAVE): U_PMR_SAVE_print(contents); break; - case (U_PMR_RESTORE): U_PMR_RESTORE_print(contents); break; - case (U_PMR_BEGINCONTAINER): U_PMR_BEGINCONTAINER_print(contents); break; - case (U_PMR_BEGINCONTAINERNOPARAMS): U_PMR_BEGINCONTAINERNOPARAMS_print(contents); break; - case (U_PMR_ENDCONTAINER): U_PMR_ENDCONTAINER_print(contents); break; - case (U_PMR_SETWORLDTRANSFORM): U_PMR_SETWORLDTRANSFORM_print(contents); break; - case (U_PMR_RESETWORLDTRANSFORM): U_PMR_RESETWORLDTRANSFORM_print(contents); break; - case (U_PMR_MULTIPLYWORLDTRANSFORM): U_PMR_MULTIPLYWORLDTRANSFORM_print(contents); break; - case (U_PMR_TRANSLATEWORLDTRANSFORM): U_PMR_TRANSLATEWORLDTRANSFORM_print(contents); break; - case (U_PMR_SCALEWORLDTRANSFORM): U_PMR_SCALEWORLDTRANSFORM_print(contents); break; - case (U_PMR_ROTATEWORLDTRANSFORM): U_PMR_ROTATEWORLDTRANSFORM_print(contents); break; - case (U_PMR_SETPAGETRANSFORM): U_PMR_SETPAGETRANSFORM_print(contents); break; - case (U_PMR_RESETCLIP): U_PMR_RESETCLIP_print(contents); break; - case (U_PMR_SETCLIPRECT): U_PMR_SETCLIPRECT_print(contents); break; - case (U_PMR_SETCLIPPATH): U_PMR_SETCLIPPATH_print(contents); break; - case (U_PMR_SETCLIPREGION): U_PMR_SETCLIPREGION_print(contents); break; - case (U_PMR_OFFSETCLIP): U_PMR_OFFSETCLIP_print(contents); break; - case (U_PMR_DRAWDRIVERSTRING): U_PMR_DRAWDRIVERSTRING_print(contents); break; - case (U_PMR_STROKEFILLPATH): U_PMR_STROKEFILLPATH_print(contents); break; - case (U_PMR_SERIALIZABLEOBJECT): U_PMR_SERIALIZABLEOBJECT_print(contents); break; - case (U_PMR_SETTSGRAPHICS): U_PMR_SETTSGRAPHICS_print(contents); break; - case (U_PMR_SETTSCLIP): U_PMR_SETTSCLIP_print(contents); break; - } + case (U_PMR_HEADER): rstatus = U_PMR_HEADER_print(contents); break; + case (U_PMR_ENDOFFILE): rstatus = U_PMR_ENDOFFILE_print(contents); + U_OA_release(&ObjCont); break; + case (U_PMR_COMMENT): rstatus = U_PMR_COMMENT_print(contents); break; + case (U_PMR_GETDC): rstatus = U_PMR_GETDC_print(contents); break; + case (U_PMR_MULTIFORMATSTART): rstatus = U_PMR_MULTIFORMATSTART_print(contents); break; + case (U_PMR_MULTIFORMATSECTION): rstatus = U_PMR_MULTIFORMATSECTION_print(contents); break; + case (U_PMR_MULTIFORMATEND): rstatus = U_PMR_MULTIFORMATEND_print(contents); break; + case (U_PMR_OBJECT): rstatus = U_PMR_OBJECT_print(contents,blimit,&ObjCont,0); break; + case (U_PMR_CLEAR): rstatus = U_PMR_CLEAR_print(contents); break; + case (U_PMR_FILLRECTS): rstatus = U_PMR_FILLRECTS_print(contents); break; + case (U_PMR_DRAWRECTS): rstatus = U_PMR_DRAWRECTS_print(contents); break; + case (U_PMR_FILLPOLYGON): rstatus = U_PMR_FILLPOLYGON_print(contents); break; + case (U_PMR_DRAWLINES): rstatus = U_PMR_DRAWLINES_print(contents); break; + case (U_PMR_FILLELLIPSE): rstatus = U_PMR_FILLELLIPSE_print(contents); break; + case (U_PMR_DRAWELLIPSE): rstatus = U_PMR_DRAWELLIPSE_print(contents); break; + case (U_PMR_FILLPIE): rstatus = U_PMR_FILLPIE_print(contents); break; + case (U_PMR_DRAWPIE): rstatus = U_PMR_DRAWPIE_print(contents); break; + case (U_PMR_DRAWARC): rstatus = U_PMR_DRAWARC_print(contents); break; + case (U_PMR_FILLREGION): rstatus = U_PMR_FILLREGION_print(contents); break; + case (U_PMR_FILLPATH): rstatus = U_PMR_FILLPATH_print(contents); break; + case (U_PMR_DRAWPATH): rstatus = U_PMR_DRAWPATH_print(contents); break; + case (U_PMR_FILLCLOSEDCURVE): rstatus = U_PMR_FILLCLOSEDCURVE_print(contents); break; + case (U_PMR_DRAWCLOSEDCURVE): rstatus = U_PMR_DRAWCLOSEDCURVE_print(contents); break; + case (U_PMR_DRAWCURVE): rstatus = U_PMR_DRAWCURVE_print(contents); break; + case (U_PMR_DRAWBEZIERS): rstatus = U_PMR_DRAWBEZIERS_print(contents); break; + case (U_PMR_DRAWIMAGE): rstatus = U_PMR_DRAWIMAGE_print(contents); break; + case (U_PMR_DRAWIMAGEPOINTS): rstatus = U_PMR_DRAWIMAGEPOINTS_print(contents); break; + case (U_PMR_DRAWSTRING): rstatus = U_PMR_DRAWSTRING_print(contents); break; + case (U_PMR_SETRENDERINGORIGIN): rstatus = U_PMR_SETRENDERINGORIGIN_print(contents); break; + case (U_PMR_SETANTIALIASMODE): rstatus = U_PMR_SETANTIALIASMODE_print(contents); break; + case (U_PMR_SETTEXTRENDERINGHINT): rstatus = U_PMR_SETTEXTRENDERINGHINT_print(contents); break; + case (U_PMR_SETTEXTCONTRAST): rstatus = U_PMR_SETTEXTCONTRAST_print(contents); break; + case (U_PMR_SETINTERPOLATIONMODE): rstatus = U_PMR_SETINTERPOLATIONMODE_print(contents); break; + case (U_PMR_SETPIXELOFFSETMODE): rstatus = U_PMR_SETPIXELOFFSETMODE_print(contents); break; + case (U_PMR_SETCOMPOSITINGMODE): rstatus = U_PMR_SETCOMPOSITINGMODE_print(contents); break; + case (U_PMR_SETCOMPOSITINGQUALITY): rstatus = U_PMR_SETCOMPOSITINGQUALITY_print(contents); break; + case (U_PMR_SAVE): rstatus = U_PMR_SAVE_print(contents); break; + case (U_PMR_RESTORE): rstatus = U_PMR_RESTORE_print(contents); break; + case (U_PMR_BEGINCONTAINER): rstatus = U_PMR_BEGINCONTAINER_print(contents); break; + case (U_PMR_BEGINCONTAINERNOPARAMS): rstatus = U_PMR_BEGINCONTAINERNOPARAMS_print(contents); break; + case (U_PMR_ENDCONTAINER): rstatus = U_PMR_ENDCONTAINER_print(contents); break; + case (U_PMR_SETWORLDTRANSFORM): rstatus = U_PMR_SETWORLDTRANSFORM_print(contents); break; + case (U_PMR_RESETWORLDTRANSFORM): rstatus = U_PMR_RESETWORLDTRANSFORM_print(contents); break; + case (U_PMR_MULTIPLYWORLDTRANSFORM): rstatus = U_PMR_MULTIPLYWORLDTRANSFORM_print(contents); break; + case (U_PMR_TRANSLATEWORLDTRANSFORM): rstatus = U_PMR_TRANSLATEWORLDTRANSFORM_print(contents); break; + case (U_PMR_SCALEWORLDTRANSFORM): rstatus = U_PMR_SCALEWORLDTRANSFORM_print(contents); break; + case (U_PMR_ROTATEWORLDTRANSFORM): rstatus = U_PMR_ROTATEWORLDTRANSFORM_print(contents); break; + case (U_PMR_SETPAGETRANSFORM): rstatus = U_PMR_SETPAGETRANSFORM_print(contents); break; + case (U_PMR_RESETCLIP): rstatus = U_PMR_RESETCLIP_print(contents); break; + case (U_PMR_SETCLIPRECT): rstatus = U_PMR_SETCLIPRECT_print(contents); break; + case (U_PMR_SETCLIPPATH): rstatus = U_PMR_SETCLIPPATH_print(contents); break; + case (U_PMR_SETCLIPREGION): rstatus = U_PMR_SETCLIPREGION_print(contents); break; + case (U_PMR_OFFSETCLIP): rstatus = U_PMR_OFFSETCLIP_print(contents); break; + case (U_PMR_DRAWDRIVERSTRING): rstatus = U_PMR_DRAWDRIVERSTRING_print(contents); break; + case (U_PMR_STROKEFILLPATH): rstatus = U_PMR_STROKEFILLPATH_print(contents); break; + case (U_PMR_SERIALIZABLEOBJECT): rstatus = U_PMR_SERIALIZABLEOBJECT_print(contents); break; + case (U_PMR_SETTSGRAPHICS): rstatus = U_PMR_SETTSGRAPHICS_print(contents); break; + case (U_PMR_SETTSCLIP): rstatus = U_PMR_SETTSCLIP_print(contents); break; + } + if(!rstatus)status=-1; return(status); } /** \brief Print data from a U_PMF_CMN_HDR object \return number of bytes in record, 0 on error + \param contents pointer to a buffer holding this EMF+ record \param Header Header of the record \param precnum EMF+ record number in file. \param off Offset in file to the start of this EMF+ record. common structure present at the beginning of all(*) EMF+ records */ -int U_PMF_CMN_HDR_print(U_PMF_CMN_HDR Header, int precnum, int off){ - printf(" %-29srec+:%5d type:%X offset:%8d rsize:%8u dsize:%8u flags:%4.4X\n", - U_pmr_names(Header.Type &U_PMR_TYPE_MASK),precnum, Header.Type,off,Header.Size,Header.DataSize,Header.Flags); +int U_PMF_CMN_HDR_print(const char *contents, U_PMF_CMN_HDR Header, int precnum, int off){ + printf(" %-29srec+:%5d type:%X offset:%8d rsize:%8u dsize:%8u flags:%4.4X crc32:%8.8X\n", + U_pmr_names(Header.Type &U_PMR_TYPE_MASK),precnum, Header.Type,off,Header.Size,Header.DataSize,Header.Flags, + lu_crc32(contents,Header.Size)); return((int) Header.Size); } @@ -446,15 +461,16 @@ int U_PMF_NODETYPE_print(int Type){ \brief Print data from a U_PMF_BRUSH object \return 1 on success, 0 on error \param contents Record from which to print data + \param blimit one byte past the end of data EMF+ manual 2.2.1.1, Microsoft name: EmfPlusBrush Object */ -int U_PMF_BRUSH_print(const char *contents){ +int U_PMF_BRUSH_print(const char *contents, const char *blimit){ uint32_t Version, Type; const char *Data; - int status = U_PMF_BRUSH_get(contents, &Version, &Type, &Data); + int status = U_PMF_BRUSH_get(contents, &Version, &Type, &Data, blimit); if(status){ printf(" + Brush:"); - (void) U_PMF_GRAPHICSVERSION_print((char *)&Version); + (void) U_PMF_GRAPHICSVERSION_memsafe_print((char *)&Version);; printf(" Type:%X(",Type); (void) U_PMF_BRUSHTYPEENUMERATION_print(Type); printf(")"); @@ -464,19 +480,19 @@ int U_PMF_BRUSH_print(const char *contents){ break; case U_BT_HatchFill: printf("\n"); - status = U_PMF_HATCHBRUSHDATA_print(Data); + status = U_PMF_HATCHBRUSHDATA_print(Data, blimit); break; case U_BT_TextureFill: printf("\n"); - status = U_PMF_TEXTUREBRUSHDATA_print(Data); + status = U_PMF_TEXTUREBRUSHDATA_print(Data, blimit); break; case U_BT_PathGradient: printf("\n"); - status = U_PMF_PATHGRADIENTBRUSHDATA_print(Data); + status = U_PMF_PATHGRADIENTBRUSHDATA_print(Data, blimit); break; case U_BT_LinearGradient: printf("\n"); - status = U_PMF_LINEARGRADIENTBRUSHDATA_print(Data); + status = U_PMF_LINEARGRADIENTBRUSHDATA_print(Data, blimit); break; default: status = 0; @@ -491,23 +507,24 @@ int U_PMF_BRUSH_print(const char *contents){ \return 1 on success, 0 on error \param contents Record from which to print data \param Which A string which is either "Start" or "End". + \param blimit one byte past the end of data EMF+ manual 2.2.1.2, Microsoft name: EmfPlusCustomLineCap Object */ -int U_PMF_CUSTOMLINECAP_print(const char *contents, const char *Which){ +int U_PMF_CUSTOMLINECAP_print(const char *contents, const char *Which, const char *blimit){ uint32_t Version, Type; const char *Data; - int status = U_PMF_CUSTOMLINECAP_get(contents, &Version, &Type, &Data); + int status = U_PMF_CUSTOMLINECAP_get(contents, &Version, &Type, &Data, blimit); if(status){ printf(" + %sLineCap:",Which); - (void) U_PMF_GRAPHICSVERSION_print((char *)&Version); + (void) U_PMF_GRAPHICSVERSION_memsafe_print((char *)&Version);; printf(", Type %X\n",Type); switch(Type){ case U_CLCDT_Default: - status = U_PMF_CUSTOMLINECAPDATA_print(Data); + status = U_PMF_CUSTOMLINECAPDATA_print(Data, blimit); break; case U_CLCDT_AdjustableArrow: - status = U_PMF_CUSTOMLINECAPARROWDATA_print(Data); + status = U_PMF_CUSTOMLINECAPARROWDATA_print(Data, blimit); break; default: status = 0; @@ -520,18 +537,19 @@ int U_PMF_CUSTOMLINECAP_print(const char *contents, const char *Which){ \brief Print data from a U_PMF_FONT object \return 1 on success, 0 on error \param contents Record from which to print data + \param blimit one byte past the end of data EMF+ manual 2.2.1.3, Microsoft name: EmfPlusFont Object */ -int U_PMF_FONT_print(const char *contents){ +int U_PMF_FONT_print(const char *contents, const char *blimit){ uint32_t Version, SizeUnit, Length; U_FLOAT EmSize; int32_t FSFlags; const char *Data; char *string; - int status = U_PMF_FONT_get(contents, &Version, &EmSize, &SizeUnit, &FSFlags, &Length, &Data); + int status = U_PMF_FONT_get(contents, &Version, &EmSize, &SizeUnit, &FSFlags, &Length, &Data, blimit); if(status){ printf(" + Font:"); - (void) U_PMF_GRAPHICSVERSION_print((char *)&Version); + (void) U_PMF_GRAPHICSVERSION_memsafe_print((char *)&Version);; printf(" EmSize:%f ", EmSize ); printf(" SizeUnit:%d ",SizeUnit); printf(" FSFlags:%d ", FSFlags ); @@ -553,25 +571,27 @@ int U_PMF_FONT_print(const char *contents){ \brief Print data from a U_PMF_IMAGE object \return 1 on success, 0 on error \param contents Record from which to print data + \param blimit one byte past the end of data + EMF+ manual 2.2.1.4, Microsoft name: EmfPlusImage Object */ -int U_PMF_IMAGE_print(const char *contents){ +int U_PMF_IMAGE_print(const char *contents, const char *blimit){ uint32_t Version, Type; const char *Data; - int status = U_PMF_IMAGE_get(contents, &Version, &Type, &Data); + int status = U_PMF_IMAGE_get(contents, &Version, &Type, &Data, blimit); if(status){ printf(" + Image:"); - (void) U_PMF_GRAPHICSVERSION_print((char *)&Version); + (void) U_PMF_GRAPHICSVERSION_memsafe_print((char *)&Version);; printf(" Type:%X\n",Type); switch(Type){ case U_IDT_Unknown: printf(" + Unknown Image Type\n"); break; case U_IDT_Bitmap: - status = U_PMF_BITMAP_print(Data); + status = U_PMF_BITMAP_print(Data, blimit); break; case U_IDT_Metafile: - status = U_PMF_METAFILE_print(Data); + status = U_PMF_METAFILE_print(Data, blimit); break; default: status = 0; @@ -585,15 +605,16 @@ int U_PMF_IMAGE_print(const char *contents){ \brief Print data from a U_PMF_IMAGEATTRIBUTES object \return 1 on success, 0 on error \param contents Record from which to print data + \param blimit one byte past the end of data EMF+ manual 2.2.1.5, Microsoft name: EmfPlusImageAttributes Object */ -int U_PMF_IMAGEATTRIBUTES_print(const char *contents){ +int U_PMF_IMAGEATTRIBUTES_print(const char *contents, const char *blimit){ uint32_t Version, WrapMode, ClampColor, ObjectClamp; - int status = U_PMF_IMAGEATTRIBUTES_get(contents, &Version, &WrapMode, &ClampColor, &ObjectClamp); + int status = U_PMF_IMAGEATTRIBUTES_get(contents, &Version, &WrapMode, &ClampColor, &ObjectClamp, blimit); if(status){ printf(" + Image Attributes: "); - (void) U_PMF_GRAPHICSVERSION_print((char *)&Version); + (void) U_PMF_GRAPHICSVERSION_memsafe_print((char *)&Version);; printf(" WrapMode:%X", WrapMode); printf(" ClampColor:%X", ClampColor); printf(" ObjectClamp:%X\n", ObjectClamp); @@ -607,20 +628,21 @@ int U_PMF_IMAGEATTRIBUTES_print(const char *contents){ \brief Print data from a U_PMF_PATH object \return 1 on success, 0 on error \param contents Record from which to print data + \param blimit one byte past the end of data EMF+ manual 2.2.1.6, Microsoft name: EmfPlusPath Object */ -int U_PMF_PATH_print(const char *contents){ +int U_PMF_PATH_print(const char *contents, const char *blimit){ unsigned int i, pos; uint32_t Version, Count; uint16_t Flags; const char *Points; const char *Types; - int status = U_PMF_PATH_get(contents, &Version, &Count, &Flags, &Points, &Types); + int status = U_PMF_PATH_get(contents, &Version, &Count, &Flags, &Points, &Types, blimit); if(status){ printf(" + Path: Version:%X Count:%d Flags:%X\n",Version, Count, Flags); /* Points part */ - U_PMF_VARPOINTS_print(&Points, Flags, Count); + U_PMF_VARPOINTS_print(&Points, Flags, Count, blimit); /* Types part */ printf(" + Types:"); @@ -631,12 +653,12 @@ int U_PMF_PATH_print(const char *contents){ is which type. */ if(Flags & U_PPF_R){ printf(" %u:",pos); - pos += U_PMF_PATHPOINTTYPERLE_print(Types); + pos += U_PMF_PATHPOINTTYPERLE_print(Types, blimit); Types+=2; } else { printf(" %d:",i); - (void) U_PMF_PATHPOINTTYPE_print(Types); + (void) U_PMF_PATHPOINTTYPE_print(Types, blimit); Types++; } } @@ -651,15 +673,15 @@ int U_PMF_PATH_print(const char *contents){ \param contents Record from which to print data EMF+ manual 2.2.1.7, Microsoft name: EmfPlusPen Object */ -int U_PMF_PEN_print(const char *contents){ +int U_PMF_PEN_print(const char *contents, const char *blimit){ uint32_t Version, Type; const char *PenData; const char *Brush; - int status = U_PMF_PEN_get(contents, &Version, &Type, &PenData, &Brush); + int status = U_PMF_PEN_get(contents, &Version, &Type, &PenData, &Brush, blimit); if(status){ printf(" + Pen: Version:%X Type:%d\n",Version,Type); - (void) U_PMF_PENDATA_print(PenData); - (void) U_PMF_BRUSH_print(Brush); + (void) U_PMF_PENDATA_print(PenData, blimit); + (void) U_PMF_BRUSH_print(Brush, blimit); } return(status); } @@ -668,17 +690,19 @@ int U_PMF_PEN_print(const char *contents){ \brief Print data from a U_PMF_REGION object \return 1 on success, 0 on error \param contents Record from which to print data + \param blimit one byte past the end of data + EMF+ manual 2.2.1.8, Microsoft name: EmfPlusRegion Object */ -int U_PMF_REGION_print(const char *contents){ +int U_PMF_REGION_print(const char *contents, const char *blimit){ uint32_t Version, Count; const char *Nodes; - int status = U_PMF_REGION_get(contents, &Version, &Count, &Nodes); + int status = U_PMF_REGION_get(contents, &Version, &Count, &Nodes, blimit); if(status){ printf(" + "); - (void) U_PMF_GRAPHICSVERSION_print((char *) &Version); + (void) U_PMF_GRAPHICSVERSION_memsafe_print((char *)&Version);; printf(" ChildNodes:%d",Count); - (void) U_PMF_REGIONNODE_print(Nodes, 1); /* 1 == top level*/ + (void) U_PMF_REGIONNODE_print(Nodes, 1, blimit); /* 1 == top level*/ } return(status); } @@ -687,12 +711,13 @@ int U_PMF_REGION_print(const char *contents){ \brief Print data from a U_PMF_STRINGFORMAT object \return 1 on success, 0 on error \param contents Record from which to print data + \param blimit one byte past the end of data EMF+ manual 2.2.1.9, Microsoft name: EmfPlusStringFormat Object */ -int U_PMF_STRINGFORMAT_print(const char *contents){ +int U_PMF_STRINGFORMAT_print(const char *contents, const char *blimit){ U_PMF_STRINGFORMAT Sfs; const char *Data; - int status = U_PMF_STRINGFORMAT_get(contents, &Sfs, &Data); + int status = U_PMF_STRINGFORMAT_get(contents, &Sfs, &Data, blimit); if(status){ printf(" + StringFormat: "); printf(" Version:%X", Sfs.Version ); @@ -710,7 +735,7 @@ int U_PMF_STRINGFORMAT_print(const char *contents){ printf(" Trimming:%X", Sfs.Trimming ); printf(" TabStopCount:%u", Sfs.TabStopCount ); printf(" RangeCount:%u", Sfs.RangeCount ); - (void) U_PMF_STRINGFORMATDATA_print(Data, Sfs.TabStopCount, Sfs.RangeCount); + (void) U_PMF_STRINGFORMATDATA_print(Data, Sfs.TabStopCount, Sfs.RangeCount, blimit); } return(status); } @@ -719,11 +744,13 @@ int U_PMF_STRINGFORMAT_print(const char *contents){ \brief Print data from a U_PMF_ARGB object \return 1 on success, 0 on error \param contents Record from which to print data + + Does not accept a blimit value. EMF+ manual 2.2.2.1, Microsoft name: EmfPlusARGB Object */ int U_PMF_ARGB_print(const char *contents){ uint8_t Blue, Green, Red, Alpha; - int status = U_PMF_ARGB_get(contents, &Blue, &Green, &Red, &Alpha); + int status = U_PMF_ARGB_get(contents, &Blue, &Green, &Red, &Alpha, contents + sizeof(U_RGBQUAD)); if(status){ printf(" RGBA{%2.2X,%2.2X,%2.2X,%2.2X}", Red, Green, Blue, Alpha); } @@ -735,12 +762,13 @@ int U_PMF_ARGB_print(const char *contents){ \brief Print data from a U_PMF_BITMAP object \return 1 on success, 0 on error \param contents Record from which to print data + \param blimit one byte past the end of data EMF+ manual 2.2.2.2, Microsoft name: EmfPlusBitmap Object */ -int U_PMF_BITMAP_print(const char *contents){ +int U_PMF_BITMAP_print(const char *contents, const char *blimit){ U_PMF_BITMAP Bs; const char *Data; - int status = U_PMF_BITMAP_get(contents, &Bs, &Data); + int status = U_PMF_BITMAP_get(contents, &Bs, &Data, blimit); if(status){ printf(" + Bitmap: Width:%d Height:%d Stride:%d\n",Bs.Width, Bs.Height, Bs.Stride); U_PMF_PX_FMT_ENUM_print(Bs.PxFormat); @@ -759,14 +787,15 @@ int U_PMF_BITMAP_print(const char *contents){ \brief Print data from a U_PMF_BITMAPDATA object \return 1 on success, 0 on error \param contents Record from which to print data + \param blimit one byte past the end of data EMF+ manual 2.2.2.3, Microsoft name: EmfPlusBitmapData Object */ -int U_PMF_BITMAPDATA_print(const char *contents){ +int U_PMF_BITMAPDATA_print(const char *contents, const char *blimit){ unsigned int i; U_PMF_PALETTE Ps; const char *Colors; const char *Data; - int status = U_PMF_BITMAPDATA_get(contents, &Ps, &Colors, &Data); + int status = U_PMF_BITMAPDATA_get(contents, &Ps, &Colors, &Data, blimit); if(status){ status = 0; printf(" BMData: Flags:%X, Elements:%u Colors:", Ps.Flags, Ps.Elements); @@ -781,14 +810,15 @@ int U_PMF_BITMAPDATA_print(const char *contents){ \brief Print data from a U_PMF_BLENDCOLORS object \return size in bytes on success, 0 on error \param contents Record from which to print data + \param blimit one byte past the end of data EMF+ manual 2.2.2.4, Microsoft name: EmfPlusBlendColors Object */ -int U_PMF_BLENDCOLORS_print(const char *contents){ +int U_PMF_BLENDCOLORS_print(const char *contents, const char *blimit){ unsigned int i; uint32_t Elements; U_FLOAT *Positions; const char *Colors; - int status = U_PMF_BLENDCOLORS_get(contents, &Elements, &Positions, &Colors); + int status = U_PMF_BLENDCOLORS_get(contents, &Elements, &Positions, &Colors, blimit); if(status){ printf(" + BlendColors: Entries:%d (entry,pos,color): ", Elements); for(i=0; i<Elements; i++){ @@ -808,14 +838,15 @@ int U_PMF_BLENDCOLORS_print(const char *contents){ \return size on success, 0 on error \param type Type of BlendFactors, usually H or V \param contents Record from which to print data + \param blimit one byte past the end of data EMF+ manual 2.2.2.5, Microsoft name: EmfPlusBlendFactors Object */ -int U_PMF_BLENDFACTORS_print(const char *contents, const char *type){ +int U_PMF_BLENDFACTORS_print(const char *contents, const char *type, const char *blimit){ unsigned int i; uint32_t Elements; U_FLOAT *Positions; U_FLOAT *Factors; - int status = U_PMF_BLENDFACTORS_get(contents, &Elements, &Positions, &Factors); + int status = U_PMF_BLENDFACTORS_get(contents, &Elements, &Positions, &Factors, blimit); if(status){ printf(" + BlendFactors%s: Entries:%d (entry,pos,factor): ",type, Elements); for(i=0; i<Elements; i++){ @@ -832,15 +863,16 @@ int U_PMF_BLENDFACTORS_print(const char *contents, const char *type){ \brief Print data from a U_PMF_BOUNDARYPATHDATA object \return size on success, 0 on error \param contents Record from which to print data + \param blimit one byte past the end of data EMF+ manual 2.2.2.6, Microsoft name: EmfPlusBoundaryPathData Object */ -int U_PMF_BOUNDARYPATHDATA_print(const char *contents){ +int U_PMF_BOUNDARYPATHDATA_print(const char *contents, const char * blimit){ int32_t Size; const char *Data; - int status = U_PMF_BOUNDARYPATHDATA_get(contents, &Size, &Data); + int status = U_PMF_BOUNDARYPATHDATA_get(contents, &Size, &Data, blimit); if(status){ printf(" + BoundaryPathData: Size:%d\n",Size); - (void) U_PMF_PATH_print(Data); + (void) U_PMF_PATH_print(Data, blimit); } return(status); } @@ -850,12 +882,13 @@ int U_PMF_BOUNDARYPATHDATA_print(const char *contents){ \brief Print data from a U_PMF_BOUNDARYPOINTDATA object \return size on success, 0 on error \param contents Record from which to print data + \param blimit one byte past the end of data EMF+ manual 2.2.2.7, Microsoft name: EmfPlusBoundaryPointData Object */ -int U_PMF_BOUNDARYPOINTDATA_print(const char *contents){ +int U_PMF_BOUNDARYPOINTDATA_print(const char *contents, const char *blimit){ int32_t Elements; U_PMF_POINTF *Points; - int status = U_PMF_BOUNDARYPOINTDATA_get(contents, &Elements, &Points); + int status = U_PMF_BOUNDARYPOINTDATA_get(contents, &Elements, &Points, blimit); if(status){ printf(" + BoundaryPointData: Elements:%u\n",Elements); U_PMF_VARPOINTF_S_print(Points, Elements); @@ -868,11 +901,12 @@ int U_PMF_BOUNDARYPOINTDATA_print(const char *contents){ \brief Print data from a U_PMF_CHARACTERRANGE object \return 1 on success, 0 on error \param contents Record from which to print data + \param blimit one byte past the end of data EMF+ manual 2.2.2.8, Microsoft name: EmfPlusCharacterRange Object */ -int U_PMF_CHARACTERRANGE_print(const char *contents){ +int U_PMF_CHARACTERRANGE_print(const char *contents, const char *blimit){ int32_t First, Length; - int status = U_PMF_CHARACTERRANGE_get(contents, &First, &Length); + int status = U_PMF_CHARACTERRANGE_get(contents, &First, &Length, blimit); if(status){ printf(" {%d,%d}",First,Length); } @@ -882,14 +916,15 @@ int U_PMF_CHARACTERRANGE_print(const char *contents){ /** \brief Print data from a U_PMF_DASHEDLINEDATA object \return 1 on success, 0 on error + \param blimit one byte past the end of data \param contents Record from which to print data EMF+ manual 2.2.2.9, Microsoft name: EmfPlusCompoundLineData Object */ -int U_PMF_COMPOUNDLINEDATA_print(const char *contents){ +int U_PMF_COMPOUNDLINEDATA_print(const char *contents, const char *blimit){ int32_t Elements; U_FLOAT *Widths; U_FLOAT *hold; - int status = U_PMF_COMPOUNDLINEDATA_get(contents, &Elements, &Widths); + int status = U_PMF_COMPOUNDLINEDATA_get(contents, &Elements, &Widths, blimit); if(status){ printf(" + CompoundLineData: Elements:%u {",Elements); Elements--; @@ -905,13 +940,14 @@ int U_PMF_COMPOUNDLINEDATA_print(const char *contents){ \brief Print data from a U_PMF_COMPRESSEDIMAGE object \return 1 on success, 0 on error \param contents Record from which to print data + \param blimit one byte past the end of data EMF+ manual 2.2.2.10, Microsoft name: EmfPlusCompressedImage Object This function does not do anything useful, but it is included so that all objects have a corresponding _get(). */ -int U_PMF_COMPRESSEDIMAGE_print(const char *contents){ +int U_PMF_COMPRESSEDIMAGE_print(const char *contents, const char *blimit){ const char *Data; - int status = U_PMF_COMPRESSEDIMAGE_get(contents, &Data); + int status = U_PMF_COMPRESSEDIMAGE_get(contents, &Data, blimit); if(status){ printf("CompressedImage:\n"); } @@ -922,15 +958,16 @@ int U_PMF_COMPRESSEDIMAGE_print(const char *contents){ \brief Print data from a U_PMF_CUSTOMENDCAPDATA object \return 1 on success, 0 on error \param contents Record from which to print data + \param blimit one byte past the end of data EMF+ manual 2.2.2.11, Microsoft name: EmfPlusCustomEndCapData Object */ -int U_PMF_CUSTOMENDCAPDATA_print(const char *contents){ +int U_PMF_CUSTOMENDCAPDATA_print(const char *contents, const char *blimit){ int32_t Size; const char *Data; - int status = U_PMF_CUSTOMENDCAPDATA_get(contents, &Size, &Data); + int status = U_PMF_CUSTOMENDCAPDATA_get(contents, &Size, &Data, blimit); if(status){ printf(" + CustomEndCap: Size:%d\n",Size); - (void) U_PMF_CUSTOMLINECAP_print(Data, "End"); + (void) U_PMF_CUSTOMLINECAP_print(Data, "End", blimit); } return(status); } @@ -939,11 +976,12 @@ int U_PMF_CUSTOMENDCAPDATA_print(const char *contents){ \brief Print data from a U_PMF_CUSTOMLINECAPARROWDATA object \return 1 on success, 0 on error \param contents Record from which to print data + \param blimit one byte past the end of data EMF+ manual 2.2.2.12, Microsoft name: EmfPlusCustomLineCapArrowData Object */ -int U_PMF_CUSTOMLINECAPARROWDATA_print(const char *contents){ +int U_PMF_CUSTOMLINECAPARROWDATA_print(const char *contents, const char *blimit){ U_PMF_CUSTOMLINECAPARROWDATA Ccad; - int status = U_PMF_CUSTOMLINECAPARROWDATA_get(contents, &Ccad); + int status = U_PMF_CUSTOMLINECAPARROWDATA_get(contents, &Ccad, blimit); if(status){ printf("CustomLineCapArrowData: "); printf(" Width:%f", Ccad.Width ); @@ -966,12 +1004,13 @@ int U_PMF_CUSTOMLINECAPARROWDATA_print(const char *contents){ \brief Print data from a U_PMF_CUSTOMLINECAPDATA object \return 1 on success, 0 on error \param contents Record from which to print data + \param blimit one byte past the end of data EMF+ manual 2.2.2.13, Microsoft name: EmfPlusCustomLineCapData Object */ -int U_PMF_CUSTOMLINECAPDATA_print(const char *contents){ +int U_PMF_CUSTOMLINECAPDATA_print(const char *contents, const char *blimit){ U_PMF_CUSTOMLINECAPDATA Clcd; const char *Data; - int status = U_PMF_CUSTOMLINECAPDATA_get(contents, &Clcd, &Data); + int status = U_PMF_CUSTOMLINECAPDATA_get(contents, &Clcd, &Data, blimit); if(status){ printf(" + CustomLineCapData: "); printf(" Flags:%X", Clcd.Flags ); @@ -984,7 +1023,7 @@ int U_PMF_CUSTOMLINECAPDATA_print(const char *contents){ printf(" WidthScale:%f", Clcd.WidthScale ); printf(" FillHotSpot:{%f,%f}",Clcd.FillHotSpot[0],Clcd.FillHotSpot[1]); printf(" LineHotSpot:{%f,%f}\n",Clcd.LineHotSpot[0],Clcd.LineHotSpot[1]); - (void) U_PMF_CUSTOMLINECAPOPTIONALDATA_print(Data, Clcd.Flags); + (void) U_PMF_CUSTOMLINECAPOPTIONALDATA_print(Data, Clcd.Flags, blimit); /* preceding line always emits an EOL */ } return(status); @@ -996,18 +1035,19 @@ int U_PMF_CUSTOMLINECAPDATA_print(const char *contents){ \return 1 on success, 0 on error \param contents Record from which to print data \param Flags CustomLineCapData Flags + \param blimit one byte past the end of data EMF+ manual 2.2.2.14, Microsoft name: EmfPlusCustomLineCapOptionalData Object */ -int U_PMF_CUSTOMLINECAPOPTIONALDATA_print(const char *contents, uint32_t Flags){ +int U_PMF_CUSTOMLINECAPOPTIONALDATA_print(const char *contents, uint32_t Flags, const char *blimit){ const char *FillData; const char *LineData; - int status = U_PMF_CUSTOMLINECAPOPTIONALDATA_get(contents, Flags, &FillData, &LineData); + int status = U_PMF_CUSTOMLINECAPOPTIONALDATA_get(contents, Flags, &FillData, &LineData, blimit); if(status){ /* True even if there is nothing in it! */ printf(" + CustomLineCapOptionalData:"); if(FillData || LineData){ - if(FillData){ (void) U_PMF_FILLPATHOBJ_print(FillData); } - if(LineData){ (void) U_PMF_LINEPATH_print(LineData); } + if(FillData){ (void) U_PMF_FILLPATHOBJ_print(FillData, blimit); } + if(LineData){ (void) U_PMF_LINEPATH_print(LineData, blimit); } } else { printf("None"); @@ -1021,15 +1061,16 @@ int U_PMF_CUSTOMLINECAPOPTIONALDATA_print(const char *contents, uint32_t Flags){ \brief Print data from a U_PMF_CUSTOMSTARTCAPDATA object \return 1 on success, 0 on error \param contents Record from which to print data + \param blimit one byte past the end of data EMF+ manual 2.2.2.15, Microsoft name: EmfPlusCustomStartCapData Object */ -int U_PMF_CUSTOMSTARTCAPDATA_print(const char *contents){ +int U_PMF_CUSTOMSTARTCAPDATA_print(const char *contents, const char *blimit){ int32_t Size; const char *Data; - int status = U_PMF_CUSTOMSTARTCAPDATA_get(contents, &Size, &Data); + int status = U_PMF_CUSTOMSTARTCAPDATA_get(contents, &Size, &Data, blimit); if(status){ printf(" + CustomStartCap: Size:%d ",Size); - (void) U_PMF_CUSTOMLINECAP_print(Data, "Start"); + (void) U_PMF_CUSTOMLINECAP_print(Data, "Start", blimit); } return(status); } @@ -1038,13 +1079,14 @@ int U_PMF_CUSTOMSTARTCAPDATA_print(const char *contents){ \brief Print data from a U_PMF_DASHEDLINEDATA object \return 1 on success, 0 on error \param contents Record from which to print data + \param blimit one byte past the end of data EMF+ manual 2.2.2.16, Microsoft name: EmfPlusDashedLineData Object */ -int U_PMF_DASHEDLINEDATA_print(const char *contents){ +int U_PMF_DASHEDLINEDATA_print(const char *contents, const char *blimit){ int32_t Elements; U_FLOAT *Lengths; U_FLOAT *hold; - int status = U_PMF_DASHEDLINEDATA_get(contents, &Elements, &Lengths); + int status = U_PMF_DASHEDLINEDATA_get(contents, &Elements, &Lengths, blimit); if(status){ printf(" DashedLineData: Elements:%u {",Elements); Elements--; @@ -1059,15 +1101,16 @@ int U_PMF_DASHEDLINEDATA_print(const char *contents){ \brief Print data from a U_PMF_FILLPATHOBJ object \return 1 on success, 0 on error \param contents Record from which to print data + \param blimit one byte past the end of data EMF+ manual 2.2.2.17, Microsoft name: EmfPlusFillPath Object */ -int U_PMF_FILLPATHOBJ_print(const char *contents){ +int U_PMF_FILLPATHOBJ_print(const char *contents, const char * blimit){ int32_t Size; const char *Data; - int status = U_PMF_FILLPATHOBJ_get(contents, &Size, &Data); + int status = U_PMF_FILLPATHOBJ_get(contents, &Size, &Data, blimit); if(status){ printf(" FillPathObj: Size:%d\n",Size); - if(Size){ (void) U_PMF_PATH_print(Data); } + if(Size){ (void) U_PMF_PATH_print(Data, blimit); } } return(status); } @@ -1076,12 +1119,13 @@ int U_PMF_FILLPATHOBJ_print(const char *contents){ \brief Print data from a U_PMF_FOCUSSCALEDATA object \return 1 on success, 0 on error \param contents Record from which to print data + \param blimit one byte past the end of data EMF+ manual 2.2.2.18, Microsoft name: EmfPlusFocusScaleData Object */ -int U_PMF_FOCUSSCALEDATA_print(const char *contents){ +int U_PMF_FOCUSSCALEDATA_print(const char *contents, const char *blimit){ uint32_t Count; U_FLOAT ScaleX, ScaleY; - int status = U_PMF_FOCUSSCALEDATA_get(contents, &Count, &ScaleX, &ScaleY); + int status = U_PMF_FOCUSSCALEDATA_get(contents, &Count, &ScaleX, &ScaleY, blimit); if(status){ printf(" FocusScaleData: Count:%d ScaleX:%f ScaleY:%f ",Count,ScaleX,ScaleY); } @@ -1089,14 +1133,31 @@ int U_PMF_FOCUSSCALEDATA_print(const char *contents){ } /** - \brief Print data from a U_PMF_GRAPHICSVERSION_print object + \brief Print data from a U_PMF_GRAPHICSVERSION object already known to be memory safe + \return 1 on success, 0 on error + \param contents Record from which to print data + EMF+ manual 2.2.2.19, Microsoft name: EmfPlusGraphicsVersion Object + + In this module the only time a U_PMF_GRAPHICSVERSION is printed is after it + has already been copied into a safe memory structure. This routine fakes up + a blimit for the general routine. + +*/ +int U_PMF_GRAPHICSVERSION_memsafe_print(const char *contents){ + const char *blimit = contents + sizeof(U_PMF_GRAPHICSVERSION); + return(U_PMF_GRAPHICSVERSION_print(contents, blimit)); +} + +/** + \brief Print data from a U_PMF_GRAPHICSVERSION object \return 1 on success, 0 on error \param contents Record from which to print data + \param blimit one byte past the end of data EMF+ manual 2.2.2.19, Microsoft name: EmfPlusGraphicsVersion Object */ -int U_PMF_GRAPHICSVERSION_print(const char *contents){ +int U_PMF_GRAPHICSVERSION_print(const char *contents, const char *blimit){ int Signature,GrfVersion; - int status = U_PMF_GRAPHICSVERSION_get(contents, &Signature, &GrfVersion); + int status = U_PMF_GRAPHICSVERSION_get(contents, &Signature, &GrfVersion, blimit); if(status){ printf(" MetaFileSig:%X",Signature ); printf(" GraphicsVersion:%X", GrfVersion); @@ -1106,15 +1167,16 @@ int U_PMF_GRAPHICSVERSION_print(const char *contents){ /** - \brief Print data from a U_PMF_HATCHBRUSHDATA_print object + \brief Print data from a U_PMF_HATCHBRUSHDATA object \return 1 on success, 0 on error \param contents Record from which to print data + \param blimit One byte past the last record in memory. EMF+ manual 2.2.2.20, Microsoft name: EmfPlusHatchBrushData Object */ -int U_PMF_HATCHBRUSHDATA_print(const char *contents){ +int U_PMF_HATCHBRUSHDATA_print(const char *contents, const char *blimit){ uint32_t Style; U_PMF_ARGB Foreground, Background; - int status = U_PMF_HATCHBRUSHDATA_get(contents, &Style, &Foreground, &Background); + int status = U_PMF_HATCHBRUSHDATA_get(contents, &Style, &Foreground, &Background, blimit); if(status){ printf(" + HBdata: Style:%u(",Style); U_PMF_HATCHSTYLEENUMERATION_print(Style); @@ -1148,12 +1210,13 @@ int U_PMF_LANGUAGEIDENTIFIER_print(U_PMF_LANGUAGEIDENTIFIER LId){ \brief Print data from a U_PMF_LINEARGRADIENTBRUSHDATA object \return 1 on success, 0 on error \param contents Record from which to print data + \param blimit One byte past the last record in memory. EMF+ manual 2.2.2.24, Microsoft name: EmfPlusLinearGradientBrushData Object */ -int U_PMF_LINEARGRADIENTBRUSHDATA_print(const char *contents){ +int U_PMF_LINEARGRADIENTBRUSHDATA_print(const char *contents, const char *blimit){ U_PMF_LINEARGRADIENTBRUSHDATA Lgbd; const char *Data; - int status = U_PMF_LINEARGRADIENTBRUSHDATA_get(contents, &Lgbd, &Data); + int status = U_PMF_LINEARGRADIENTBRUSHDATA_get(contents, &Lgbd, &Data, blimit); if(status){ printf(" + LinearGradientBrushData: Flags:%X WrapMode:%d Rect:",Lgbd.Flags, Lgbd.WrapMode); (void) U_PMF_RECTF_S_print(&(Lgbd.RectF)); @@ -1167,7 +1230,7 @@ int U_PMF_LINEARGRADIENTBRUSHDATA_print(const char *contents){ printf(" Reserved2:"); (void) U_PMF_ARGB_print((char *)&(Lgbd.Reserved2)); printf("\n"); - (void) U_PMF_LINEARGRADIENTBRUSHOPTIONALDATA_print(Data, Lgbd.Flags); + (void) U_PMF_LINEARGRADIENTBRUSHOPTIONALDATA_print(Data, Lgbd.Flags, blimit); } return(status); } @@ -1179,16 +1242,17 @@ int U_PMF_LINEARGRADIENTBRUSHDATA_print(const char *contents){ \return 1 on success, 0 on error \param contents Record from which to print data \param BDFlag Describes optional values in contents + \param blimit One byte past the last record in memory. EMF+ manual 2.2.2.25, Microsoft name: EmfPlusLinearGradientBrushOptionalData Object */ -int U_PMF_LINEARGRADIENTBRUSHOPTIONALDATA_print(const char *contents, int BDFlag){ +int U_PMF_LINEARGRADIENTBRUSHOPTIONALDATA_print(const char *contents, int BDFlag, const char *blimit){ U_PMF_TRANSFORMMATRIX Tm; const char *Bc; const char *BfH; const char *BfV; int None=1; printf(" + LinearGradientBrushOptionalData: "); - int status = U_PMF_LINEARGRADIENTBRUSHOPTIONALDATA_get(contents, BDFlag, &Tm, &Bc, &BfH, &BfV); + int status = U_PMF_LINEARGRADIENTBRUSHOPTIONALDATA_get(contents, BDFlag, &Tm, &Bc, &BfH, &BfV, blimit); if(status){ if(BDFlag & U_BD_Transform){ U_PMF_TRANSFORMMATRIX2_print(&Tm); @@ -1196,17 +1260,17 @@ int U_PMF_LINEARGRADIENTBRUSHOPTIONALDATA_print(const char *contents, int BDFlag } if(Bc){ printf("\n"); - (void) U_PMF_BLENDCOLORS_print(Bc); + (void) U_PMF_BLENDCOLORS_print(Bc, blimit); None=0; } if(BfH){ printf("\n"); - (void) U_PMF_BLENDFACTORS_print(BfH,"H"); + (void) U_PMF_BLENDFACTORS_print(BfH,"H", blimit); None=0; } if(BfV){ printf("\n"); - (void) U_PMF_BLENDFACTORS_print(BfV,"V"); + (void) U_PMF_BLENDFACTORS_print(BfV,"V", blimit); None=0; } if(None){ @@ -1220,15 +1284,16 @@ int U_PMF_LINEARGRADIENTBRUSHOPTIONALDATA_print(const char *contents, int BDFlag \brief Print data from a U_PMF_LINEPATH object \return 1 on success, 0 on error \param contents Record from which to print data + \param blimit one byte past the end of data EMF+ manual 2.2.2.26, Microsoft name: EmfPlusLinePath Object */ -int U_PMF_LINEPATH_print(const char *contents){ +int U_PMF_LINEPATH_print(const char *contents, const char * blimit){ int32_t Size; const char *Data; - int status = U_PMF_LINEPATH_get(contents, &Size, &Data); + int status = U_PMF_LINEPATH_get(contents, &Size, &Data, blimit); if(status){ printf(" LinePath: Size:%d\n", Size); - (void) U_PMF_PATH_print(Data); + (void) U_PMF_PATH_print(Data, blimit); } return(status); } @@ -1237,13 +1302,14 @@ int U_PMF_LINEPATH_print(const char *contents){ \brief Print data from a U_PMF_METAFILE object \return 1 on success, 0 on error \param contents Record from which to print data + \param blimit one byte past the end of data EMF+ manual 2.2.2.27, Microsoft name: EmfPlusMetafile Object */ -int U_PMF_METAFILE_print(const char *contents){ +int U_PMF_METAFILE_print(const char *contents, const char * blimit){ uint32_t Type; uint32_t Size; const char *Data; - int status = U_PMF_METAFILE_get(contents, &Type, &Size, &Data); + int status = U_PMF_METAFILE_get(contents, &Type, &Size, &Data, blimit); if(status){ printf(" MetaFile: Type:%X Size:%d",Type, Size); /* embedded metafiles are not handled beyond this*/ @@ -1255,14 +1321,15 @@ int U_PMF_METAFILE_print(const char *contents){ \brief Print data from a U_PMF_PALETTE object \return 1 on success, 0 on error \param contents Record from which to print data + \param blimit one byte past the end of data EMF+ manual 2.2.2.28, Microsoft name: EmfPlusPalette Object */ -int U_PMF_PALETTE_print(const char *contents){ +int U_PMF_PALETTE_print(const char *contents, const char *blimit){ unsigned int i; uint32_t Flags; uint32_t Elements; const char *Data; - int status = U_PMF_PALETTE_get(contents, &Flags, &Elements, &Data); + int status = U_PMF_PALETTE_get(contents, &Flags, &Elements, &Data, blimit); if(status){ printf(" Palette: Flags:%X Elements:%u Colors:",Flags, Elements); for(i=0; i<Elements; i++){ @@ -1278,15 +1345,16 @@ int U_PMF_PALETTE_print(const char *contents){ \brief Print data from a U_PMF_PATHGRADIENTBRUSHDATA object \return 1 on success, 0 on error \param contents Record from which to print data + \param blimit one byte past the end of data EMF+ manual 2.2.2.29, Microsoft name: EmfPlusPathGradientBrushData Object */ -int U_PMF_PATHGRADIENTBRUSHDATA_print(const char *contents){ +int U_PMF_PATHGRADIENTBRUSHDATA_print(const char *contents, const char *blimit){ U_PMF_PATHGRADIENTBRUSHDATA Pgbd; const char *Gradient; const char *Boundary; const char *Data=NULL; unsigned int i; - int status = U_PMF_PATHGRADIENTBRUSHDATA_get(contents, &Pgbd, &Gradient, &Boundary, &Data); + int status = U_PMF_PATHGRADIENTBRUSHDATA_get(contents, &Pgbd, &Gradient, &Boundary, &Data, blimit); if(status){ printf(" + PathGradientBrushData: Flags:%X WrapMode:%d, CenterColor:",Pgbd.Flags, Pgbd.WrapMode); (void) U_PMF_ARGB_print((char *)&(Pgbd.CenterColor)); @@ -1301,12 +1369,12 @@ int U_PMF_PATHGRADIENTBRUSHDATA_print(const char *contents){ printf("\n"); } if(Pgbd.Flags & U_BD_Path){ - (void) U_PMF_BOUNDARYPATHDATA_print(Boundary); + (void) U_PMF_BOUNDARYPATHDATA_print(Boundary, blimit); } else { - (void) U_PMF_BOUNDARYPOINTDATA_print(Boundary); + (void) U_PMF_BOUNDARYPOINTDATA_print(Boundary, blimit); } - (void) U_PMF_PATHGRADIENTBRUSHOPTIONALDATA_print(Data, Pgbd.Flags); + (void) U_PMF_PATHGRADIENTBRUSHOPTIONALDATA_print(Data, Pgbd.Flags, blimit); } return(status); } @@ -1316,25 +1384,26 @@ int U_PMF_PATHGRADIENTBRUSHDATA_print(const char *contents){ \return 1 on success, 0 on error \param contents Record from which to print data \param BDFlag Describes optional values in contents + \param blimit one byte past the end of data EMF+ manual 2.2.2.30, Microsoft name: EmfPlusPathGradientBrushOptionalData Object */ -int U_PMF_PATHGRADIENTBRUSHOPTIONALDATA_print(const char *contents, int BDFlag){ +int U_PMF_PATHGRADIENTBRUSHOPTIONALDATA_print(const char *contents, int BDFlag, const char *blimit){ if(BDFlag & (U_BD_Transform | U_BD_PresetColors | U_BD_BlendFactorsH | U_BD_FocusScales)){ printf(" + PathGradientBrushOptionalData: "); } if(BDFlag & U_BD_Transform){ - U_PMF_TRANSFORMMATRIX_print(contents); + U_PMF_TRANSFORMMATRIX_print(contents, blimit); contents += sizeof(U_PMF_TRANSFORMMATRIX); } if(BDFlag & U_BD_PresetColors){ /* If this is present, BlendFactorsH will not be */ - contents += U_PMF_BLENDCOLORS_print(contents); + contents += U_PMF_BLENDCOLORS_print(contents, blimit); } if(BDFlag & U_BD_BlendFactorsH){/* If this is present, U_BD_PresetColors will not be */ - contents += U_PMF_BLENDFACTORS_print(contents,""); + contents += U_PMF_BLENDFACTORS_print(contents,"", blimit); } if(BDFlag & U_BD_FocusScales){ - contents += U_PMF_BLENDFACTORS_print(contents,""); - U_PMF_FOCUSSCALEDATA_print(contents); + contents += U_PMF_BLENDFACTORS_print(contents,"", blimit); + U_PMF_FOCUSSCALEDATA_print(contents, blimit); } return(1); } @@ -1343,11 +1412,12 @@ int U_PMF_PATHGRADIENTBRUSHOPTIONALDATA_print(const char *contents, int BDFlag){ \brief Print data from a U_PMF_PATHPOINTTYPE object \return 1 on success, 0 on error \param contents Record from which to print data + \param blimit one byte past the end of data EMF+ manual 2.2.2.31, Microsoft name: EmfPlusPathPointType Object */ -int U_PMF_PATHPOINTTYPE_print(const char *contents){ +int U_PMF_PATHPOINTTYPE_print(const char *contents, const char *blimit){ int Flags, Type; - int status = U_PMF_PATHPOINTTYPE_get(contents, &Flags, &Type); + int status = U_PMF_PATHPOINTTYPE_get(contents, &Flags, &Type, blimit); if(status){ printf("{Flags:%X Type:",Flags); (void) U_PMF_PATHPOINTTYPE_ENUM_print(Type); @@ -1360,11 +1430,12 @@ int U_PMF_PATHPOINTTYPE_print(const char *contents){ \brief Print data from a U_PMF_PATHPOINTTYPERLE object \return Number of elements in the run, 0 on error \param contents Record from which to print data + \param blimit one byte past the end of data EMF+ manual 2.2.2.32, Microsoft name: EmfPlusPathPointTypeRLE Object */ -int U_PMF_PATHPOINTTYPERLE_print(const char *contents){ +int U_PMF_PATHPOINTTYPERLE_print(const char *contents, const char *blimit){ int Bezier, Elements, Type; - int status = U_PMF_PATHPOINTTYPERLE_get(contents, &Bezier, &Elements, &Type); + int status = U_PMF_PATHPOINTTYPERLE_get(contents, &Bezier, &Elements, &Type, blimit); if(status){ status = Elements; printf(" PathPointTypeRLE: Bezier:%c Elements:%u, Type: ",(Bezier ? 'Y' : 'N'), Elements); @@ -1378,16 +1449,17 @@ int U_PMF_PATHPOINTTYPERLE_print(const char *contents){ \brief Print data from a U_PMF_PATHPOINTTYPERLE object \return 1 on success, 0 on error \param contents Record from which to print data + \param blimit one byte past the end of data EMF+ manual 2.2.2.33, Microsoft name: EmfPlusPenData Object */ -int U_PMF_PENDATA_print(const char *contents){ +int U_PMF_PENDATA_print(const char *contents, const char *blimit){ uint32_t Flags, Unit; U_FLOAT Width; const char *Data; - int status = U_PMF_PENDATA_get(contents, &Flags, &Unit, &Width, &Data); + int status = U_PMF_PENDATA_get(contents, &Flags, &Unit, &Width, &Data, blimit); if(status){ printf(" + Pendata: Flags:%X Unit:%X Width:%f",Flags, Unit, Width); - (void) U_PMF_PENOPTIONALDATA_print(Data, Flags); /* prints a new line at end */ + (void) U_PMF_PENOPTIONALDATA_print(Data, Flags, blimit); /* prints a new line at end */ } return(status); } @@ -1398,11 +1470,12 @@ int U_PMF_PENDATA_print(const char *contents){ \brief Print data from a U_PMF_PENOPTIONALDATA object \return 1 on success, 0 on error \param contents Record from which to print data + \param blimit one byte past the end of data \param Flags PenData Flags that determine which optionaldata fields are present in the record. EMF+ manual 2.2.2.34, Microsoft name: EmfPlusPenOptionalData Object */ -int U_PMF_PENOPTIONALDATA_print(const char *contents, int Flags){ +int U_PMF_PENOPTIONALDATA_print(const char *contents, int Flags, const char *blimit){ U_PMF_TRANSFORMMATRIX Matrix; int32_t StartCap; int32_t EndCap; @@ -1431,7 +1504,8 @@ int U_PMF_PENOPTIONALDATA_print(const char *contents, int Flags){ &Alignment, &CmpndLineData, &CSCapData, - &CECapData); + &CECapData, + blimit); if(status){ if(Flags & U_PD_Transform){ (void) U_PMF_TRANSFORMMATRIX2_print(&Matrix);} if(Flags & U_PD_StartCap){ printf(" StartCap:%d", StartCap );} @@ -1441,14 +1515,14 @@ int U_PMF_PENOPTIONALDATA_print(const char *contents, int Flags){ if(Flags & U_PD_LineStyle){ printf(" Style:%X", Style );} if(Flags & U_PD_DLCap){ printf(" DLCap:%X", DLCap );} if(Flags & U_PD_DLOffset){ printf(" DLOffset:%f", DLOffset );} - if(Flags & U_PD_DLData){ (void) U_PMF_DASHEDLINEDATA_print(DLData );} + if(Flags & U_PD_DLData){ (void) U_PMF_DASHEDLINEDATA_print(DLData, blimit );} if(Flags & U_PD_NonCenter){ printf(" Alignment:%d", Alignment );} if(Flags & (U_PD_Transform | U_PD_StartCap | U_PD_EndCap | U_PD_Join | U_PD_MiterLimit | U_PD_LineStyle | U_PD_DLCap | U_PD_DLOffset |U_PD_DLData |U_PD_NonCenter)){ printf("\n"); } - if(Flags & U_PD_CLData){ (void) U_PMF_COMPOUNDLINEDATA_print(CmpndLineData); } - if(Flags & U_PD_CustomStartCap){ (void) U_PMF_CUSTOMSTARTCAPDATA_print(CSCapData); } - if(Flags & U_PD_CustomEndCap){ (void) U_PMF_CUSTOMENDCAPDATA_print(CECapData); } + if(Flags & U_PD_CLData){ (void) U_PMF_COMPOUNDLINEDATA_print(CmpndLineData, blimit); } + if(Flags & U_PD_CustomStartCap){ (void) U_PMF_CUSTOMSTARTCAPDATA_print(CSCapData, blimit); } + if(Flags & U_PD_CustomEndCap){ (void) U_PMF_CUSTOMENDCAPDATA_print(CECapData, blimit); } } return(status); } @@ -1456,11 +1530,12 @@ int U_PMF_PENOPTIONALDATA_print(const char *contents, int Flags){ \brief Print data from a U_PMF_POINT object \return 1 on success, 0 on error \param contents Record from which to print data + \param blimit one byte past the end of data EMF+ manual 2.2.2.35, Microsoft name: EmfPlusPoint Object */ -int U_PMF_POINT_print(const char **contents){ +int U_PMF_POINT_print(const char **contents, const char *blimit){ U_FLOAT X, Y; - int status = U_PMF_POINT_get(contents, &X, &Y); + int status = U_PMF_POINT_get(contents, &X, &Y, blimit); if(status){ printf("{%f,%f}", X, Y); } @@ -1483,11 +1558,12 @@ int U_PMF_POINT_S_print(U_PMF_POINT *Point){\ \brief Print data from a U_PMF_POINTF object \return 1 on success, 0 on error \param contents Record from which to print data + \param blimit one byte past the end of data EMF+ manual 2.2.2.36, Microsoft name: EmfPlusPointF Object */ -int U_PMF_POINTF_print(const char **contents){ +int U_PMF_POINTF_print(const char **contents, const char *blimit){ U_FLOAT X, Y; - int status = U_PMF_POINTF_get(contents, &X, &Y); + int status = U_PMF_POINTF_get(contents, &X, &Y, blimit); if(status){ printf("{%f,%f}", X, Y); } @@ -1512,6 +1588,7 @@ int U_PMF_POINTF_S_print(U_PMF_POINTF *Point){ \param contents Pointer to next data to print \param Xpos X coordinate for current point \param Ypos Y coordinate for current point + \param blimit one byte past the end of data On each call the next relative offset is extracted, the current coordinates are modified with that offset, and the pointer is @@ -1519,9 +1596,9 @@ int U_PMF_POINTF_S_print(U_PMF_POINTF *Point){ EMF+ manual 2.2.2.37, Microsoft name: EmfPlusPointR Object */ -int U_PMF_POINTR_print(const char **contents, U_FLOAT *Xpos, U_FLOAT *Ypos){ +int U_PMF_POINTR_print(const char **contents, U_FLOAT *Xpos, U_FLOAT *Ypos, const char *blimit){ U_FLOAT X, Y; - int status = U_PMF_POINTR_get(contents, &X, &Y); + int status = U_PMF_POINTR_get(contents, &X, &Y, blimit); *Xpos += X; *Ypos += Y; if(status){ @@ -1534,11 +1611,12 @@ int U_PMF_POINTR_print(const char **contents, U_FLOAT *Xpos, U_FLOAT *Ypos){ \brief Print data from a U_PMF_RECT object \return 1 on success, 0 on error \param contents Record from which to print data + \param blimit one byte past the end of data EMF+ manual 2.2.2.38, Microsoft name: EmfPlusRect Object */ -int U_PMF_RECT_print(const char **contents){ +int U_PMF_RECT_print(const char **contents, const char *blimit){ int16_t X, Y, Width, Height; - int status = U_PMF_RECT_get(contents, &X, &Y, &Width, &Height); + int status = U_PMF_RECT_get(contents, &X, &Y, &Width, &Height, blimit); if(status){ printf("{UL{%d,%d},WH{%d,%d}}", X, Y, Width, Height); } @@ -1560,11 +1638,12 @@ int U_PMF_RECT_S_print(U_PMF_RECT *Rect){ \brief Print data from a U_PMF_RECTF object \return 1 on success, 0 on error \param contents Record from which to print data + \param blimit one byte past the end of data EMF+ manual 2.2.2.39, Microsoft name: EmfPlusRectF Object */ -int U_PMF_RECTF_print(const char **contents){ +int U_PMF_RECTF_print(const char **contents, const char *blimit){ U_FLOAT X, Y, Width, Height; - int status = U_PMF_RECTF_get(contents, &X, &Y, &Width, &Height); + int status = U_PMF_RECTF_get(contents, &X, &Y, &Width, &Height, blimit); if(status){ printf("{UL{%f,%f},WH{%f,%f}}", X, Y, Width, Height); } @@ -1587,27 +1666,28 @@ int U_PMF_RECTF_S_print(U_PMF_RECTF *Rect){ \return size on success, 0 on error \param contents Record from which to print data \param Level Tree level. This routine is recursive and could go down many levels. 1 is the top, >1 are child nodes. + \param blimit one byte past the end of data EMF+ manual 2.2.2.40, Microsoft name: EmfPlusRegionNode Object */ -int U_PMF_REGIONNODE_print(const char *contents, int Level){ +int U_PMF_REGIONNODE_print(const char *contents, int Level, const char *blimit){ int len=4; /* Type will always be present */ uint32_t Type; const char *Data; - int status = U_PMF_REGIONNODE_get(contents, &Type, &Data); + int status = U_PMF_REGIONNODE_get(contents, &Type, &Data, blimit); if(status){ printf("\n + RegionNode(Level:%d) { Type:%X(",Level,Type); U_PMF_NODETYPE_print(Type); printf(")"); if(Type >= U_RNDT_And && Type <= U_RNDT_Complement){ - len += U_PMF_REGIONNODECHILDNODES_print(Data, Level+1); + len += U_PMF_REGIONNODECHILDNODES_print(Data, Level+1, blimit); } else if(Type == U_RNDT_Rect){ len += sizeof(U_PMF_RECTF); - (void) U_PMF_RECTF_print(&Data); + (void) U_PMF_RECTF_print(&Data, blimit); printf("\n"); } else if(Type == U_RNDT_Path){ - len += U_PMF_REGIONNODEPATH_print(Data); + len += U_PMF_REGIONNODEPATH_print(Data, blimit); } /* U_RNDT_Empty and U_RNDT_Infinite do not change the length */ else if(Type == U_RNDT_Empty ){ printf(" Empty" ); } @@ -1624,18 +1704,19 @@ int U_PMF_REGIONNODE_print(const char *contents, int Level){ \return size on success, 0 on error \param contents Record from which to print data \param Level Tree level. This routine is recursive and could go down many levels. 1 is the top, >1 are child nodes. + \param blimit one byte past the end of data EMF+ manual 2.2.2.41, Microsoft name: EmfPlusRegionNodeChildNodes Object */ -int U_PMF_REGIONNODECHILDNODES_print(const char *contents, int Level){ +int U_PMF_REGIONNODECHILDNODES_print(const char *contents, int Level, const char *blimit){ uint32_t size,rsize; printf(" RegionNodeChildNodes:\n"); printf(" + RNCN__Left(Level:%d) {", Level); - size = U_PMF_REGIONNODE_print(contents, Level); + size = U_PMF_REGIONNODE_print(contents, Level, blimit); printf(" + RNCN__Left(Level:%d) },\n", Level); if(size){ contents += size; printf(" + RNCN_Right(Level:%d) {", Level); - rsize = U_PMF_REGIONNODE_print(contents, Level); + rsize = U_PMF_REGIONNODE_print(contents, Level, blimit); size += rsize; printf(" + RNCN_Right(Level:%d) },\n",Level); } @@ -1646,15 +1727,16 @@ int U_PMF_REGIONNODECHILDNODES_print(const char *contents, int Level){ \brief Print data from a U_PMF_REGIONNODEPATH object \return Size of data on success, 0 on error \param contents Record from which to print data + \param blimit one byte past the end of data EMF+ manual 2.2.2.42, Microsoft name: EmfPlusRegionNodePath Object */ -int U_PMF_REGIONNODEPATH_print(const char *contents){ +int U_PMF_REGIONNODEPATH_print(const char *contents, const char * blimit){ int32_t Size; const char *Data; - int status = U_PMF_REGIONNODEPATH_get(contents, &Size, &Data); + int status = U_PMF_REGIONNODEPATH_get(contents, &Size, &Data, blimit); if(status){ printf(" RegionNodePath: \n"); - (void) U_PMF_PATH_print(Data); + (void) U_PMF_PATH_print(Data, blimit); status = Size + 4; /* data sizee + the 4 bytes encoding the size */ } return(status); @@ -1665,11 +1747,12 @@ int U_PMF_REGIONNODEPATH_print(const char *contents){ \brief Print data from a U_PMF_SOLIDBRUSHDATA object \return 1 on success, 0 on error \param contents Record from which to print data + \param blimit one byte past the end of data EMF+ manual 2.2.2.43, Microsoft name: EmfPlusSolidBrushData Object */ -int U_PMF_SOLIDBRUSHDATA_print(const char *contents){ +int U_PMF_SOLIDBRUSHDATA_print(const char *contents, const char *blimit){ U_PMF_ARGB Color; - int status = U_PMF_SOLIDBRUSHDATA_get(contents, &Color); + int status = U_PMF_SOLIDBRUSHDATA_get(contents, &Color, blimit); if(status){ printf(" SolidBrushData: "); (void) U_PMF_ARGB_print((char *) &Color); @@ -1683,12 +1766,13 @@ int U_PMF_SOLIDBRUSHDATA_print(const char *contents){ \param contents Record from which to print data \param TabStopCount Entries in TabStop array \param RangeCount Entries in CharRange array + \param blimit one byte past the end of data EMF+ manual 2.2.2.44, Microsoft name: EmfPlusStringFormatData Object */ -int U_PMF_STRINGFORMATDATA_print(const char *contents, uint32_t TabStopCount, uint32_t RangeCount){ +int U_PMF_STRINGFORMATDATA_print(const char *contents, uint32_t TabStopCount, uint32_t RangeCount, const char *blimit){ const U_FLOAT *TabStops; const U_PMF_CHARACTERRANGE *CharRange; - int status = U_PMF_STRINGFORMATDATA_get(contents, TabStopCount, RangeCount, &TabStops, &CharRange); + int status = U_PMF_STRINGFORMATDATA_get(contents, TabStopCount, RangeCount, &TabStops, &CharRange, blimit); if(status){ printf(" SFdata: TabStopCount:%u RangeCount:%u\n", TabStopCount, RangeCount); @@ -1708,13 +1792,14 @@ int U_PMF_STRINGFORMATDATA_print(const char *contents, uint32_t TabStopCount, ui \brief Print data from a U_PMF_TEXTUREBRUSHDATA object \return 1 on success, 0 on error \param contents Record from which to print data + \param blimit one byte past the end of data EMF+ manual 2.2.2.45, Microsoft name: EmfPlusTextureBrushData Object */ -int U_PMF_TEXTUREBRUSHDATA_print(const char *contents){ +int U_PMF_TEXTUREBRUSHDATA_print(const char *contents, const char *blimit){ uint32_t Flags; int32_t WrapMode; const char *Data; - int status = U_PMF_TEXTUREBRUSHDATA_get(contents, &Flags, &WrapMode, &Data); + int status = U_PMF_TEXTUREBRUSHDATA_get(contents, &Flags, &WrapMode, &Data, blimit); if(status){ printf(" + TBdata: Flags:%X WrapMode:%d", Flags, WrapMode); } @@ -1725,18 +1810,23 @@ int U_PMF_TEXTUREBRUSHDATA_print(const char *contents){ \brief Print data from a U_PMF_TEXTUREBRUSHOPTIONALDATA object \return 1 on success, 0 on error \param contents Record from which to print data + \param HasMatrix True if the record contains a matrix. \param HasImage True if the record contains an image. + \param blimit one byte past the end of data EMF+ manual 2.2.2.46, Microsoft name: EmfPlusTextureBrushOptionalData Object */ -int U_PMF_TEXTUREBRUSHOPTIONALDATA_print(const char *contents, int HasImage){ +int U_PMF_TEXTUREBRUSHOPTIONALDATA_print(const char *contents, int HasMatrix, int HasImage, const char *blimit){ U_PMF_TRANSFORMMATRIX Matrix; + U_PMF_TRANSFORMMATRIX *pMatrix; const char *Image; - int status = U_PMF_TEXTUREBRUSHOPTIONALDATA_get(contents, HasImage, &Matrix, &Image); + if(HasMatrix){ pMatrix = &Matrix; } + else { pMatrix = NULL; } + int status = U_PMF_TEXTUREBRUSHOPTIONALDATA_get(contents, HasImage, pMatrix, &Image, blimit); if(status){ printf(" + TBOptdata: Image:%c", (HasImage ? 'Y' : 'N')); (void) U_PMF_TRANSFORMMATRIX2_print(&Matrix); - (void) U_PMF_IMAGE_print(Image); + (void) U_PMF_IMAGE_print(Image, blimit); } return(status); } @@ -1745,11 +1835,12 @@ int U_PMF_TEXTUREBRUSHOPTIONALDATA_print(const char *contents, int HasImage){ \brief Print data from a U_PMF_TRANSFORMMATRIX object stored in file byte order. \return 1 on success, 0 on error \param contents Record from which to print data + \param blimit one byte past the end of data EMF+ manual 2.2.2.47, Microsoft name: EmfPlusTransformMatrix Object */ -int U_PMF_TRANSFORMMATRIX_print(const char *contents){ +int U_PMF_TRANSFORMMATRIX_print(const char *contents, const char *blimit){ U_PMF_TRANSFORMMATRIX Tm; - int status = U_PMF_TRANSFORMMATRIX_get(contents, &Tm); + int status = U_PMF_TRANSFORMMATRIX_get(contents, &Tm, blimit); if(status){ U_PMF_TRANSFORMMATRIX2_print(&Tm); } @@ -1763,7 +1854,12 @@ int U_PMF_TRANSFORMMATRIX_print(const char *contents){ EMF+ manual 2.2.2.47, Microsoft name: EmfPlusTransformMatrix Object */ int U_PMF_TRANSFORMMATRIX2_print(U_PMF_TRANSFORMMATRIX *Tm){ - printf(" Matrix:{%f,%f,%f,%f,%f,%f}", Tm->m11, Tm->m12, Tm->m21, Tm->m22, Tm->dX, Tm->dY); + if(Tm){ + printf(" Matrix:{%f,%f,%f,%f,%f,%f}", Tm->m11, Tm->m12, Tm->m21, Tm->m22, Tm->dX, Tm->dY); + } + else { + printf(" Matrix:(None)"); + } return(1); } @@ -1782,12 +1878,13 @@ int U_PMF_ROTMATRIX2_print(U_PMF_ROTMATRIX *Rm){ \brief Print data from a U_PMF_IE_BLUR object \return 1 on success, 0 on error \param contents Record from which to print data + \param blimit one byte past the end of data EMF+ manual 2.2.3.1, Microsoft name: BlurEffect Object */ -int U_PMF_IE_BLUR_print(const char *contents){ +int U_PMF_IE_BLUR_print(const char *contents, const char *blimit){ U_FLOAT Radius; uint32_t ExpandEdge; - int status = U_PMF_IE_BLUR_get(contents, &Radius, &ExpandEdge); + int status = U_PMF_IE_BLUR_get(contents, &Radius, &ExpandEdge, blimit); if(status){ printf("BlurEffect Radius:%f ExpandEdge:%u\n", Radius, ExpandEdge); } @@ -1798,11 +1895,12 @@ int U_PMF_IE_BLUR_print(const char *contents){ \brief Print data from a U_PMF_IE_BRIGHTNESSCONTRAST object \return 1 on success, 0 on error \param contents Record from which to print data + \param blimit one byte past the end of data EMF+ manual 2.2.3.2, Microsoft name: BrightnessContrastEffect Object */ -int U_PMF_IE_BRIGHTNESSCONTRAST_print(const char *contents){ +int U_PMF_IE_BRIGHTNESSCONTRAST_print(const char *contents, const char *blimit){ int32_t Brightness, Contrast; - int status = U_PMF_IE_BRIGHTNESSCONTRAST_get(contents, &Brightness, &Contrast); + int status = U_PMF_IE_BRIGHTNESSCONTRAST_get(contents, &Brightness, &Contrast, blimit); if(status){ printf("BrightnessContrastEffect Brightness:%d Contrast:%d\n", Brightness, Contrast); } @@ -1813,11 +1911,12 @@ int U_PMF_IE_BRIGHTNESSCONTRAST_print(const char *contents){ \brief Print data from a U_PMF_IE_COLORBALANCE object \return 1 on success, 0 on error \param contents Record from which to print data + \param blimit one byte past the end of data EMF+ manual 2.2.3.3, Microsoft name: ColorBalanceEffect Object */ -int U_PMF_IE_COLORBALANCE_print(const char *contents){ +int U_PMF_IE_COLORBALANCE_print(const char *contents, const char *blimit){ int32_t CyanRed, MagentaGreen, YellowBlue; - int status = U_PMF_IE_COLORBALANCE_get(contents, &CyanRed, &MagentaGreen, &YellowBlue); + int status = U_PMF_IE_COLORBALANCE_get(contents, &CyanRed, &MagentaGreen, &YellowBlue, blimit); if(status){ printf("ColorBalanceEffect CyanRed:%d MagentaGreen:%d YellowBlue:%d\n", CyanRed, MagentaGreen, YellowBlue); } @@ -1828,12 +1927,13 @@ int U_PMF_IE_COLORBALANCE_print(const char *contents){ \brief Print data from a U_PMF_IE_COLORCURVE object \return 1 on success, 0 on error \param contents Record from which to print data + \param blimit one byte past the end of data EMF+ manual 2.2.3.4, Microsoft name: ColorCurveEffect Object */ -int U_PMF_IE_COLORCURVE_print(const char *contents){ +int U_PMF_IE_COLORCURVE_print(const char *contents, const char *blimit){ uint32_t Adjust, Channel; int32_t Intensity; - int status = U_PMF_IE_COLORCURVE_get(contents, &Adjust, &Channel, &Intensity); + int status = U_PMF_IE_COLORCURVE_get(contents, &Adjust, &Channel, &Intensity, blimit); if(status){ printf("ColorBalanceEffect Adjust:%u Channel:%u Intensity:%d\n", Adjust, Channel, Intensity); } @@ -1844,11 +1944,12 @@ int U_PMF_IE_COLORCURVE_print(const char *contents){ \brief Print data from a U_PMF_IE_COLORLOOKUPTABLE object \return 1 on success, 0 on error \param contents Record from which to print data + \param blimit one byte past the end of data EMF+ manual 2.2.3.5, Microsoft name: ColorLookupTableEffect Object */ -int U_PMF_IE_COLORLOOKUPTABLE_print(const char *contents){ +int U_PMF_IE_COLORLOOKUPTABLE_print(const char *contents, const char *blimit){ const uint8_t *BLUT, *GLUT, *RLUT, *ALUT; - int status = U_PMF_IE_COLORLOOKUPTABLE_get(contents, &BLUT, &GLUT, &RLUT, &ALUT); + int status = U_PMF_IE_COLORLOOKUPTABLE_get(contents, &BLUT, &GLUT, &RLUT, &ALUT, blimit); if(status){ printf("ColorLookupTableEffect \n"); U_PMF_UINT8_ARRAY_print(" BLUT:", BLUT, 256, "\n"); @@ -1863,12 +1964,13 @@ int U_PMF_IE_COLORLOOKUPTABLE_print(const char *contents){ \brief Print data from a U_PMF_IE_COLORMATRIX object \return 1 on success, 0 on error \param contents Record from which to print data + \param blimit one byte past the end of data EMF+ manual 2.2.3.6, Microsoft name: ColorMatrixEffect Object */ -int U_PMF_IE_COLORMATRIX_print(const char *contents){ +int U_PMF_IE_COLORMATRIX_print(const char *contents, const char *blimit){ U_PMF_IE_COLORMATRIX Matrix; int i,j; - int status = U_PMF_IE_COLORMATRIX_get(contents, &Matrix); + int status = U_PMF_IE_COLORMATRIX_get(contents, &Matrix, blimit); if(status){ printf("ColorMatrixEffect\n"); for(i=0;i<5;i++){ @@ -1885,11 +1987,12 @@ int U_PMF_IE_COLORMATRIX_print(const char *contents){ \brief Print data from a U_PMF_IE_HUESATURATIONLIGHTNESS object \return 1 on success, 0 on error \param contents Record from which to print data + \param blimit one byte past the end of data EMF+ manual 2.2.3.7, Microsoft name: HueSaturationLightnessEffect Object */ -int U_PMF_IE_HUESATURATIONLIGHTNESS_print(const char *contents){ +int U_PMF_IE_HUESATURATIONLIGHTNESS_print(const char *contents, const char *blimit){ int32_t Hue, Saturation, Lightness; - int status = U_PMF_IE_HUESATURATIONLIGHTNESS_get(contents, &Hue, &Saturation, &Lightness); + int status = U_PMF_IE_HUESATURATIONLIGHTNESS_get(contents, &Hue, &Saturation, &Lightness, blimit); if(status){ printf("HueSaturationLightnessEffect Hue:%d Saturation:%d Lightness:%d\n", Hue, Saturation, Lightness); } @@ -1900,11 +2003,12 @@ int U_PMF_IE_HUESATURATIONLIGHTNESS_print(const char *contents){ \brief Print data from a U_PMF_IE_LEVELS object \return 1 on success, 0 on error \param contents Record from which to print data + \param blimit one byte past the end of data EMF+ manual 2.2.3.8, Microsoft name: LevelsEffect Object */ -int U_PMF_IE_LEVELS_print(const char *contents){ +int U_PMF_IE_LEVELS_print(const char *contents, const char *blimit){ int32_t Highlight, Midtone, Shadow; - int status = U_PMF_IE_LEVELS_get(contents, &Highlight, &Midtone, &Shadow); + int status = U_PMF_IE_LEVELS_get(contents, &Highlight, &Midtone, &Shadow, blimit); if(status){ printf("LevelEffect Highlight:%d Midtone:%d Shadow:%d\n", Highlight, Midtone, Shadow); } @@ -1915,12 +2019,13 @@ int U_PMF_IE_LEVELS_print(const char *contents){ \brief Print data from a U_PMF_IE_REDEYECORRECTION object \return 1 on success, 0 on error \param contents Record from which to print data + \param blimit one byte past the end of data EMF+ manual 2.2.3.9, Microsoft name: RedEyeCorrectionEffect Object */ -int U_PMF_IE_REDEYECORRECTION_print(const char *contents){ +int U_PMF_IE_REDEYECORRECTION_print(const char *contents, const char *blimit){ int32_t Elements; U_RECTL *Rects; - int status = U_PMF_IE_REDEYECORRECTION_get(contents, &Elements, &Rects); + int status = U_PMF_IE_REDEYECORRECTION_get(contents, &Elements, &Rects, blimit); if(status){ printf("RedEyeCorrectionEffect Elements:%u", Elements); for(; Elements; Elements--, Rects++){ @@ -1935,13 +2040,14 @@ int U_PMF_IE_REDEYECORRECTION_print(const char *contents){ /** \brief Print data from a U_PMF_IE_SHARPEN object \return 1 on success, 0 on error + \param blimit one byte past the end of data \param contents Record from which to print data EMF+ manual 2.2.3.10, Microsoft name: SharpenEffect Object */ -int U_PMF_IE_SHARPEN_print(const char *contents){ +int U_PMF_IE_SHARPEN_print(const char *contents, const char *blimit){ U_FLOAT Radius; int32_t Sharpen; - int status = U_PMF_IE_SHARPEN_get(contents, &Radius, &Sharpen); + int status = U_PMF_IE_SHARPEN_get(contents, &Radius, &Sharpen, blimit); if(status){ printf("SharpenEffect Radius:%f Sharpen:%u\n", Radius, Sharpen); } @@ -1952,11 +2058,12 @@ int U_PMF_IE_SHARPEN_print(const char *contents){ \brief Print data from a U_PMF_IE_TINT object \return 1 on success, 0 on error \param contents Record from which to print data + \param blimit one byte past the end of data EMF+ manual 2.2.3.11, Microsoft name: TintEffect Object */ -int U_PMF_IE_TINT_print(const char *contents){ +int U_PMF_IE_TINT_print(const char *contents, const char *blimit){ int32_t Hue, Amount; - int status = U_PMF_IE_TINT_get(contents, &Hue, &Amount); + int status = U_PMF_IE_TINT_get(contents, &Hue, &Amount, blimit); if(status){ printf("TintEffect Hue:%d Amount:%d\n", Hue, Amount); } @@ -1981,6 +2088,9 @@ int U_PMR_OFFSETCLIP_print(const char *contents){ if(status){ printf(" + dx:%f dy:%f\n",dX,dY); } + else { + printf(" corrupt record\n"); + } return(status); } @@ -2007,6 +2117,9 @@ int U_PMR_SETCLIPPATH_print(const char *contents){ if(status){ printf(" + PathID:%u CMenum:%d\n",PathID,CMenum); } + else { + printf(" corrupt record\n"); + } return(status); } @@ -2027,6 +2140,9 @@ int U_PMR_SETCLIPRECT_print(const char *contents){ U_PMF_RECTF_S_print(&Rect); printf("\n"); } + else { + printf(" corrupt record\n"); + } return(status); } @@ -2045,6 +2161,9 @@ int U_PMR_SETCLIPREGION_print(const char *contents){ U_PMF_COMBINEMODEENUMERATION_print(CMenum); printf(")\n"); } + else { + printf(" corrupt record\n"); + } return(status); } @@ -2061,6 +2180,11 @@ int U_PMR_COMMENT_print(const char *contents){ unsigned int i=0; int status = U_PMR_COMMENT_get(contents, &Header, &Data); if(status){ + const char *blimit = contents + Header.Size; + if(IS_MEM_UNSAFE(Data, Header.DataSize, blimit)){ + printf(" corrupt record\n"); + return(0); + } /* try to print it, but only ASCII, bail on anything that is not ASCII */ printf(" + Data:"); for(i=0; i< Header.DataSize; i++,Data++){ @@ -2070,6 +2194,9 @@ int U_PMR_COMMENT_print(const char *contents){ } printf("\n"); } + else { + printf(" corrupt record\n"); + } return(status); } @@ -2107,9 +2234,12 @@ int U_PMR_HEADER_print(const char *contents){ if(status){ /* try to print it, but only ASCII, bail on anything that is not ASCII */ printf(" + "); - (void) U_PMF_GRAPHICSVERSION_print((char *) &Version); + (void) U_PMF_GRAPHICSVERSION_memsafe_print((char *)&Version);; printf(" IsDual:%c IsVideo:%d LogicalDpiX,y:{%u,%u}\n",(IsDual ? 'Y' : 'N'),IsVideo,LogicalDpiX, LogicalDpiY); } + else { + printf(" corrupt record\n"); + } return(status); } @@ -2127,6 +2257,9 @@ int U_PMR_CLEAR_print(const char *contents){ printf(" + Color:"); (void) U_PMF_ARGB_print((char *) &Color); } + else { + printf(" corrupt record\n"); + } return(status); } @@ -2147,6 +2280,9 @@ int U_PMR_DRAWARC_print(const char *contents){ (void) U_PMF_VARRECTF_S_print(&Rect, 1); printf("\n"); } + else { + printf(" corrupt record\n"); + } return(status); } @@ -2167,6 +2303,9 @@ int U_PMR_DRAWBEZIERS_print(const char *contents){ U_PMF_VARPOINTF_S_print(Points, Elements); free(Points); } + else { + printf(" corrupt record\n"); + } return(status); } @@ -2194,6 +2333,9 @@ int U_PMR_DRAWCLOSEDCURVE_print(const char *contents){ U_PMF_VARPOINTF_S_print(Points, Elements); free(Points); } + else { + printf(" corrupt record\n"); + } return(status); } @@ -2221,6 +2363,9 @@ int U_PMR_DRAWCURVE_print(const char *contents){ U_PMF_VARPOINTF_S_print(Points, Elements); free(Points); } + else { + printf(" corrupt record\n"); + } return(status); } @@ -2269,7 +2414,9 @@ int U_PMR_DRAWDRIVERSTRING_print(const char *contents){ free(Matrix); printf("\n"); } - + } + else { + printf(" corrupt record\n"); } return(status); } @@ -2290,6 +2437,9 @@ int U_PMR_DRAWELLIPSE_print(const char *contents){ (void) U_PMF_VARRECTF_S_print(&Rect, 1); printf("\n"); } + else { + printf(" corrupt record\n"); + } return(status); } @@ -2313,6 +2463,9 @@ int U_PMR_DRAWIMAGE_print(const char *contents){ (void) U_PMF_RECTF_S_print(&DstRect); printf("\n"); } + else { + printf(" corrupt record\n"); + } return(status); } @@ -2339,6 +2492,9 @@ int U_PMR_DRAWIMAGEPOINTS_print(const char *contents){ U_PMF_VARPOINTF_S_print(Points, Elements); free(Points); } + else { + printf(" corrupt record\n"); + } return(status); } @@ -2360,6 +2516,9 @@ int U_PMR_DRAWLINES_print(const char *contents){ U_PMF_VARPOINTF_S_print(Points, Elements); free(Points); } + else { + printf(" corrupt record\n"); + } return(status); } @@ -2375,6 +2534,9 @@ int U_PMR_DRAWPATH_print(const char *contents){ if(status){ printf(" + PathIdx:%d PenIdx:%d\n", PathIdx, PenIdx); } + else { + printf(" corrupt record\n"); + } return(status); } @@ -2395,6 +2557,9 @@ int U_PMR_DRAWPIE_print(const char *contents){ (void) U_PMF_VARRECTF_S_print(&Rect, 1); printf("\n"); } + else { + printf(" corrupt record\n"); + } return(status); } @@ -2402,10 +2567,9 @@ int U_PMR_DRAWPIE_print(const char *contents){ \brief Print data from a U_PMR_DRAWRECTS record \return size of record in bytes on success, 0 on error \param contents Record from which to print data - \param blimit One byte past the last record in memory. EMF+ manual 2.3.4.13, Microsoft name: EmfPlusDrawRects Record, Index 0x0B */ -int U_PMR_DRAWRECTS_print(const char *contents, const char *blimit){ +int U_PMR_DRAWRECTS_print(const char *contents){ uint32_t PenID; int ctype; uint32_t Elements; @@ -2413,11 +2577,13 @@ int U_PMR_DRAWRECTS_print(const char *contents, const char *blimit){ U_PMF_CMN_HDR hdr; int status = U_PMR_DRAWRECTS_get(contents, &hdr, &PenID, &ctype, &Elements, &Rects); if(status){ - if(contents + hdr.Size >= blimit)return(0); printf(" + PenID:%u ctype:%d Elements:%u Rect:", PenID,ctype,Elements); (void) U_PMF_VARRECTF_S_print(Rects, Elements); printf("\n"); } + else { + printf(" corrupt record\n"); + } if(Rects)free(Rects); return(status); } @@ -2452,6 +2618,9 @@ int U_PMR_DRAWSTRING_print(const char *contents){ printf(" String(as_UTF8):(none)\n"); } } + else { + printf(" corrupt record\n"); + } return(status); } @@ -2477,6 +2646,9 @@ int U_PMR_FILLCLOSEDCURVE_print(const char *contents){ U_PMF_VARPOINTF_S_print(Points, Elements); free(Points); } + else { + printf(" corrupt record\n"); + } return(status); } @@ -2498,6 +2670,9 @@ int U_PMR_FILLELLIPSE_print(const char *contents){ (void) U_PMF_VARRECTF_S_print(&Rect, 1); printf("\n"); } + else { + printf(" corrupt record\n"); + } return(status); } @@ -2516,6 +2691,9 @@ int U_PMR_FILLPATH_print(const char *contents){ (void) U_PMF_VARBRUSHID_print(btype, BrushID); printf("\n"); } + else { + printf(" corrupt record\n"); + } return(status); } @@ -2537,6 +2715,9 @@ int U_PMR_FILLPIE_print(const char *contents){ (void) U_PMF_VARRECTF_S_print(&Rect, 1); printf("\n"); } + else { + printf(" corrupt record\n"); + } return(status); } @@ -2558,6 +2739,9 @@ int U_PMR_FILLPOLYGON_print(const char *contents){ U_PMF_VARPOINTF_S_print(Points, Elements); free(Points); } + else { + printf(" corrupt record\n"); + } return(status); } @@ -2565,23 +2749,24 @@ int U_PMR_FILLPOLYGON_print(const char *contents){ \brief Print data from a U_PMR_FILLRECTS record \return size of record in bytes on success, 0 on error \param contents Record from which to print data - \param blimit One byte past the last record in memory. EMF+ manual 2.3.4.20, Microsoft name: EmfPlusFillRects Record, Index 0x0A */ -int U_PMR_FILLRECTS_print(const char *contents, const char *blimit){ +int U_PMR_FILLRECTS_print(const char *contents){ int btype, ctype; uint32_t BrushID, Elements; U_PMF_RECTF *Rects; U_PMF_CMN_HDR hdr; int status = U_PMR_FILLRECTS_get(contents, &hdr, &btype,&ctype, &BrushID, &Elements, &Rects); if(status){ - if(contents + hdr.Size >= blimit)return(0); printf(" + btype:%d ctype:%d Elements:%u",btype,ctype,Elements); (void) U_PMF_VARBRUSHID_print(btype, BrushID); (void) U_PMF_VARRECTF_S_print(Rects, Elements); free(Rects); printf("\n"); } + else { + printf(" corrupt record\n"); + } return(status); } @@ -2600,6 +2785,9 @@ int U_PMR_FILLREGION_print(const char *contents){ (void) U_PMF_VARBRUSHID_print(btype, BrushID); printf("\n"); } + else { + printf(" corrupt record\n"); + } return(status); } @@ -2611,6 +2799,10 @@ int U_PMR_FILLREGION_print(const char *contents){ \param ObjCont Structure that holds accumulated object. \param term Flag used when an abnormal termination of a series of continuation records is encountered. EMF+ manual 2.3.5.1, Microsoft name: EmfPlusObject Record, Index 0x13 + + This is the ONLY EMF+ record type which needs an explicit blimit passed in. Since it glues + together multiple records, and calls itself recursively, the initial caller cannot determine + that all of the sizes will be OK from the first record's header. */ int U_PMR_OBJECT_print(const char *contents, const char *blimit, U_OBJ_ACCUM *ObjCont, int term){ U_PMF_CMN_HDR Header; @@ -2641,8 +2833,11 @@ int U_PMR_OBJECT_print(const char *contents, const char *blimit, U_OBJ_ACCUM *Ob /* In a corrupt EMF+ file we might hit a new type of record before all the continuation records expected have been found. If that happens terminate whatever we have accumulated so far, and then go on to emit the new (unexpected) record. */ - if(contents + Header.Size >= blimit)return(0); - if(!status)return(status); + if(IS_MEM_UNSAFE(contents, Header.Size, blimit))return(0); + if(!status){ + printf(" corrupt record\n"); + return(status); + } if((ObjCont->used > 0) && (U_OA_append(ObjCont, NULL, 0, otype, ObjID) < 0)){ U_PMR_OBJECT_print(contents, blimit, ObjCont, 1); } @@ -2665,16 +2860,17 @@ int U_PMR_OBJECT_print(const char *contents, const char *blimit, U_OBJ_ACCUM *Ob ttype = otype; } if(status){ + blimit = ObjCont->accum + ObjCont->used; /* more restrictive blimit, just to end of object */ switch(ttype){ - case U_OT_Brush: (void) U_PMF_BRUSH_print(ObjCont->accum); break; - case U_OT_Pen: (void) U_PMF_PEN_print(ObjCont->accum); break; - case U_OT_Path: (void) U_PMF_PATH_print(ObjCont->accum); break; - case U_OT_Region: (void) U_PMF_REGION_print(ObjCont->accum); break; - case U_OT_Image: (void) U_PMF_IMAGE_print(ObjCont->accum); break; - case U_OT_Font: (void) U_PMF_FONT_print(ObjCont->accum); break; - case U_OT_StringFormat: (void) U_PMF_STRINGFORMAT_print(ObjCont->accum); break; - case U_OT_ImageAttributes: (void) U_PMF_IMAGEATTRIBUTES_print(ObjCont->accum); break; - case U_OT_CustomLineCap: (void) U_PMF_CUSTOMLINECAP_print(ObjCont->accum,""); break; + case U_OT_Brush: (void) U_PMF_BRUSH_print(ObjCont->accum, blimit); break; + case U_OT_Pen: (void) U_PMF_PEN_print(ObjCont->accum, blimit); break; + case U_OT_Path: (void) U_PMF_PATH_print(ObjCont->accum, blimit); break; + case U_OT_Region: (void) U_PMF_REGION_print(ObjCont->accum, blimit); break; + case U_OT_Image: (void) U_PMF_IMAGE_print(ObjCont->accum, blimit); break; + case U_OT_Font: (void) U_PMF_FONT_print(ObjCont->accum, blimit); break; + case U_OT_StringFormat: (void) U_PMF_STRINGFORMAT_print(ObjCont->accum, blimit); break; + case U_OT_ImageAttributes: (void) U_PMF_IMAGEATTRIBUTES_print(ObjCont->accum, blimit); break; + case U_OT_CustomLineCap: (void) U_PMF_CUSTOMLINECAP_print(ObjCont->accum,"", blimit); break; case U_OT_Invalid: default: printf("INVALID OBJECT TYPE!!!!\n"); @@ -2707,24 +2903,27 @@ int U_PMR_SERIALIZABLEOBJECT_print(const char *contents){ printf("\n + Effect:"); free(string); switch(iee){ - case U_IEE_Unknown: printf("(undefined)\n"); break; - case U_IEE_BlurEffectGuid: U_PMF_IE_BLUR_print(Data); break; - case U_IEE_BrightnessContrastEffectGuid: U_PMF_IE_BRIGHTNESSCONTRAST_print(Data); break; - case U_IEE_ColorBalanceEffectGuid: U_PMF_IE_COLORBALANCE_print(Data); break; - case U_IEE_ColorCurveEffectGuid: U_PMF_IE_COLORCURVE_print(Data); break; - case U_IEE_ColorLookupTableEffectGuid: U_PMF_IE_COLORLOOKUPTABLE_print(Data); break; - case U_IEE_ColorMatrixEffectGuid: U_PMF_IE_COLORMATRIX_print(Data); break; - case U_IEE_HueSaturationLightnessEffectGuid: U_PMF_IE_HUESATURATIONLIGHTNESS_print(Data); break; - case U_IEE_LevelsEffectGuid: U_PMF_IE_LEVELS_print(Data); break; - case U_IEE_RedEyeCorrectionEffectGuid: U_PMF_IE_REDEYECORRECTION_print(Data); break; - case U_IEE_SharpenEffectGuid: U_PMF_IE_SHARPEN_print(Data); break; - case U_IEE_TintEffectGuid: U_PMF_IE_TINT_print(Data); break; + case U_IEE_Unknown: printf("(undefined)\n"); break; + case U_IEE_BlurEffectGuid: U_PMF_IE_BLUR_print(Data, Data + sizeof(U_PMF_IE_BLUR)); break; + case U_IEE_BrightnessContrastEffectGuid: U_PMF_IE_BRIGHTNESSCONTRAST_print(Data, Data + sizeof(U_PMF_IE_BRIGHTNESSCONTRAST)); break; + case U_IEE_ColorBalanceEffectGuid: U_PMF_IE_COLORBALANCE_print(Data, Data + sizeof(U_PMF_IE_COLORBALANCE)); break; + case U_IEE_ColorCurveEffectGuid: U_PMF_IE_COLORCURVE_print(Data, Data + sizeof(U_PMF_IE_COLORCURVE)); break; + case U_IEE_ColorLookupTableEffectGuid: U_PMF_IE_COLORLOOKUPTABLE_print(Data, Data + sizeof(U_PMF_IE_COLORLOOKUPTABLE)); break; + case U_IEE_ColorMatrixEffectGuid: U_PMF_IE_COLORMATRIX_print(Data, Data + sizeof(U_PMF_IE_COLORMATRIX)); break; + case U_IEE_HueSaturationLightnessEffectGuid: U_PMF_IE_HUESATURATIONLIGHTNESS_print(Data, Data + sizeof(U_PMF_IE_HUESATURATIONLIGHTNESS)); break; + case U_IEE_LevelsEffectGuid: U_PMF_IE_LEVELS_print(Data, Data + sizeof(U_PMF_IE_LEVELS)); break; + case U_IEE_RedEyeCorrectionEffectGuid: U_PMF_IE_REDEYECORRECTION_print(Data, Data + sizeof(U_PMF_IE_REDEYECORRECTION)); break; + case U_IEE_SharpenEffectGuid: U_PMF_IE_SHARPEN_print(Data, Data + sizeof(U_PMF_IE_SHARPEN)); break; + case U_IEE_TintEffectGuid: U_PMF_IE_TINT_print(Data, Data + sizeof(U_PMF_IE_TINT)); break; } } else { printf(" + GUID:ERROR Size:%u\n",Size); } } + else { + printf(" corrupt record\n"); + } return(status); } @@ -2740,6 +2939,9 @@ int U_PMR_SETANTIALIASMODE_print(const char *contents){ if(status){ printf(" + SMenum:%d AntiAlias:%c\n",SMenum,(aatype ? 'Y' : 'N')); } + else { + printf(" corrupt record\n"); + } return(status); } @@ -2755,6 +2957,9 @@ int U_PMR_SETCOMPOSITINGMODE_print(const char *contents){ if(status){ printf(" + CMenum:%d\n",CMenum); } + else { + printf(" corrupt record\n"); + } return(status); } @@ -2770,6 +2975,9 @@ int U_PMR_SETCOMPOSITINGQUALITY_print(const char *contents){ if(status){ printf(" + CQenum:%d\n",CQenum); } + else { + printf(" corrupt record\n"); + } return(status); } @@ -2785,6 +2993,9 @@ int U_PMR_SETINTERPOLATIONMODE_print(const char *contents){ if(status){ printf(" + IMenum:%d\n",IMenum); } + else { + printf(" corrupt record\n"); + } return(status); } @@ -2800,6 +3011,9 @@ int U_PMR_SETPIXELOFFSETMODE_print(const char *contents){ if(status){ printf(" + POMenum:%d\n",POMenum); } + else { + printf(" corrupt record\n"); + } return(status); } @@ -2815,6 +3029,9 @@ int U_PMR_SETRENDERINGORIGIN_print(const char *contents){ if(status){ printf(" + X:%d Y:%d\n", X, Y); } + else { + printf(" corrupt record\n"); + } return(status); } @@ -2830,6 +3047,9 @@ int U_PMR_SETTEXTCONTRAST_print(const char *contents){ if(status){ printf(" + GC:%d\n", GC); } + else { + printf(" corrupt record\n"); + } return(status); } @@ -2845,6 +3065,9 @@ int U_PMR_SETTEXTRENDERINGHINT_print(const char *contents){ if(status){ printf(" + TRHenum:%d\n",TRHenum); } + else { + printf(" corrupt record\n"); + } return(status); } @@ -2865,6 +3088,9 @@ int U_PMR_BEGINCONTAINER_print(const char *contents){ printf(" SrcRect:"); (void) U_PMF_RECTF_S_print(&SrcRect); printf(" StackID:%u\n", StackID); } + else { + printf(" corrupt record\n"); + } return(status); } @@ -2880,6 +3106,9 @@ int U_PMR_BEGINCONTAINERNOPARAMS_print(const char *contents){ if(status){ printf(" + StackID:%u\n", StackID); } + else { + printf(" corrupt record\n"); + } return(status); } @@ -2895,6 +3124,9 @@ int U_PMR_ENDCONTAINER_print(const char *contents){ if(status){ printf(" + StackID:%u\n", StackID); } + else { + printf(" corrupt record\n"); + } return(status); } @@ -2910,6 +3142,9 @@ int U_PMR_RESTORE_print(const char *contents){ if(status){ printf(" + StackID:%u\n", StackID); } + else { + printf(" corrupt record\n"); + } return(status); } @@ -2925,6 +3160,9 @@ int U_PMR_SAVE_print(const char *contents){ if(status){ printf(" + StackID:%u\n", StackID); } + else { + printf(" corrupt record\n"); + } return(status); } @@ -2945,6 +3183,9 @@ int U_PMR_SETTSCLIP_print(const char *contents){ free(Rects); printf("\n"); } + else { + printf(" corrupt record\n"); + } return(status); } @@ -2967,6 +3208,7 @@ int U_PMR_SETTSGRAPHICS_print(const char *contents){ &RenderOriginX, &RenderOriginY, &TextContrast, &FilterType, &PixelOffset, &WorldToDevice, &Data); if(status){ + const char *blimit = contents + status; printf(" + vgatype:%d pptype:%u",vgatype,pptype); printf(" AntiAliasMode:%u TextRenderHint:%u CompositingMode:%u CompositingQuality:%u", AntiAliasMode, TextRenderHint, CompositingMode, CompositingQuality); @@ -2974,9 +3216,12 @@ int U_PMR_SETTSGRAPHICS_print(const char *contents){ printf(" TextContrast:%u",TextContrast); printf(" WorldToDevice:"); U_PMF_TRANSFORMMATRIX2_print(&WorldToDevice); - if(pptype){ (void) U_PMF_PALETTE_print(Data); } + if(pptype && !U_PMF_PALETTE_print(Data, blimit))return(0); printf("\n"); } + else { + printf(" corrupt record\n"); + } return(status); } @@ -2996,6 +3241,9 @@ int U_PMR_MULTIPLYWORLDTRANSFORM_print(const char *contents){ U_PMF_TRANSFORMMATRIX2_print(&Matrix); printf("\n"); } + else { + printf(" corrupt record\n"); + } return(status); } @@ -3022,6 +3270,9 @@ int U_PMR_ROTATEWORLDTRANSFORM_print(const char *contents){ if(status){ printf(" + xmtype:%d Multiply:%s Angle:%f\n",xmtype,(xmtype ? "Post" : "Pre"), Angle); } + else { + printf(" corrupt record\n"); + } return(status); } @@ -3038,6 +3289,9 @@ int U_PMR_SCALEWORLDTRANSFORM_print(const char *contents){ if(status){ printf(" + xmtype:%d Multiply:%s ScaleX:%f ScaleY:%f\n",xmtype,(xmtype ? "Post" : "Pre"), Sx, Sy); } + else { + printf(" corrupt record\n"); + } return(status); } @@ -3054,6 +3308,9 @@ int U_PMR_SETPAGETRANSFORM_print(const char *contents){ if(status){ printf(" + UTenum:%d Scale:%f\n",UTenum, Scale); } + else { + printf(" corrupt record\n"); + } return(status); } @@ -3071,6 +3328,9 @@ int U_PMR_SETWORLDTRANSFORM_print(const char *contents){ U_PMF_TRANSFORMMATRIX2_print(&Matrix); printf("\n"); } + else { + printf(" corrupt record\n"); + } return(status); } @@ -3087,6 +3347,9 @@ int U_PMR_TRANSLATEWORLDTRANSFORM_print(const char *contents){ if(status){ printf(" + xmtype:%d Multiply:%s TranslateX:%f TranlateY:%f\n",xmtype,(xmtype ? "Post" : "Pre"), Dx, Dy); } + else { + printf(" corrupt record\n"); + } return(status); } diff --git a/src/libuemf/upmf_print.h b/src/libuemf/upmf_print.h index 3ba6694eb..a25374487 100644 --- a/src/libuemf/upmf_print.h +++ b/src/libuemf/upmf_print.h @@ -6,11 +6,11 @@ /* File: upmf_print.h -Version: 0.0.2 -Date: 17-OCT-2013 +Version: 0.0.5 +Date: 28-APR-2015 Author: David Mathog, Biology Division, Caltech email: mathog@caltech.edu -Copyright: 2013 David Mathog and California Institute of Technology (Caltech) +Copyright: 2015 David Mathog and California Institute of Technology (Caltech) */ #ifndef _UPMF_PRINT_ @@ -23,7 +23,7 @@ extern "C" { #include "upmf.h" /* includes uemf.h */ /* prototypes for simple types and enums used in PMR records */ -int U_PMF_CMN_HDR_print(U_PMF_CMN_HDR Header, int precnum, int off); +int U_PMF_CMN_HDR_print(const char *contents, U_PMF_CMN_HDR Header, int precnum, int off); int U_PMF_UINT8_ARRAY_print(const char *Start, const uint8_t *Array, int Elements, char *End); int U_PMF_BRUSHTYPEENUMERATION_print(int otype); int U_PMF_HATCHSTYLEENUMERATION_print(int hstype); @@ -33,77 +33,78 @@ int U_PMF_PX_FMT_ENUM_print(int pfe); int U_PMF_NODETYPE_print(int Type); /* prototypes for objects used in PMR records */ -int U_PMF_BRUSH_print(const char *contents); -int U_PMF_CUSTOMLINECAP_print(const char *contents, const char *Which); -int U_PMF_FONT_print(const char *contents); -int U_PMF_IMAGE_print(const char *contents); -int U_PMF_IMAGEATTRIBUTES_print(const char *contents); -int U_PMF_PATH_print(const char *contents); -int U_PMF_PEN_print(const char *contents); -int U_PMF_REGION_print(const char *contents); -int U_PMF_STRINGFORMAT_print(const char *contents); +int U_PMF_BRUSH_print(const char *contents, const char *blimit); +int U_PMF_CUSTOMLINECAP_print(const char *contents, const char *Which, const char *blimit); +int U_PMF_FONT_print(const char *contents, const char *blimit); +int U_PMF_IMAGE_print(const char *contents, const char *blimit); +int U_PMF_IMAGEATTRIBUTES_print(const char *contents, const char *blimit); +int U_PMF_PATH_print(const char *contents, const char *blimit); +int U_PMF_PEN_print(const char *contents, const char *blimit); +int U_PMF_REGION_print(const char *contents, const char *blimit); +int U_PMF_STRINGFORMAT_print(const char *contents, const char *blimit); int U_PMF_ARGB_print(const char *contents); -int U_PMF_BITMAP_print(const char *contents); -int U_PMF_BITMAPDATA_print(const char *contents); -int U_PMF_BLENDCOLORS_print(const char *contents); -int U_PMF_BLENDFACTORS_print(const char *contents, const char *type); -int U_PMF_BOUNDARYPATHDATA_print(const char *contents); -int U_PMF_BOUNDARYPOINTDATA_print(const char *contents); -int U_PMF_CHARACTERRANGE_print(const char *contents); -int U_PMF_COMPOUNDLINEDATA_print(const char *contents); -int U_PMF_COMPRESSEDIMAGE_print(const char *contents); -int U_PMF_CUSTOMENDCAPDATA_print(const char *contents); -int U_PMF_CUSTOMLINECAPARROWDATA_print(const char *contents); -int U_PMF_CUSTOMLINECAPDATA_print(const char *contents); -int U_PMF_CUSTOMLINECAPOPTIONALDATA_print(const char *contents, uint32_t Flags); -int U_PMF_CUSTOMSTARTCAPDATA_print(const char *contents); -int U_PMF_DASHEDLINEDATA_print(const char *contents); -int U_PMF_FILLPATHOBJ_print(const char *contents); -int U_PMF_FOCUSSCALEDATA_print(const char *contents); -int U_PMF_GRAPHICSVERSION_print(const char *contents); -int U_PMF_HATCHBRUSHDATA_print(const char *contents); +int U_PMF_BITMAP_print(const char *contents, const char *blimit); +int U_PMF_BITMAPDATA_print(const char *contents, const char *blimit); +int U_PMF_BLENDCOLORS_print(const char *contents, const char *blimit); +int U_PMF_BLENDFACTORS_print(const char *contents, const char *type, const char *blimit); +int U_PMF_BOUNDARYPATHDATA_print(const char *contents, const char *blimit); +int U_PMF_BOUNDARYPOINTDATA_print(const char *contents, const char *blimit); +int U_PMF_CHARACTERRANGE_print(const char *contents, const char *blimit); +int U_PMF_COMPOUNDLINEDATA_print(const char *contents, const char *blimit); +int U_PMF_COMPRESSEDIMAGE_print(const char *contents, const char *blimit); +int U_PMF_CUSTOMENDCAPDATA_print(const char *contents, const char *blimit); +int U_PMF_CUSTOMLINECAPARROWDATA_print(const char *contents, const char *blimit); +int U_PMF_CUSTOMLINECAPDATA_print(const char *contents, const char *blimit); +int U_PMF_CUSTOMLINECAPOPTIONALDATA_print(const char *contents, uint32_t Flags, const char *blimit); +int U_PMF_CUSTOMSTARTCAPDATA_print(const char *contents, const char *blimit); +int U_PMF_DASHEDLINEDATA_print(const char *contents, const char *blimit); +int U_PMF_FILLPATHOBJ_print(const char *contents, const char *blimit); +int U_PMF_FOCUSSCALEDATA_print(const char *contents, const char *blimit); +int U_PMF_GRAPHICSVERSION_memsafe_print(const char *contents); +int U_PMF_GRAPHICSVERSION_print(const char *contents, const char *blimit); +int U_PMF_HATCHBRUSHDATA_print(const char *contents, const char *blimit); int U_PMF_LANGUAGEIDENTIFIER_print(U_PMF_LANGUAGEIDENTIFIER LId); -int U_PMF_LINEARGRADIENTBRUSHDATA_print(const char *contents); -int U_PMF_LINEARGRADIENTBRUSHOPTIONALDATA_print(const char *contents, int BDFlag); -int U_PMF_LINEPATH_print(const char *contents); -int U_PMF_METAFILE_print(const char *contents); -int U_PMF_PALETTE_print(const char *contents); -int U_PMF_PATHGRADIENTBRUSHDATA_print(const char *contents); -int U_PMF_PATHGRADIENTBRUSHOPTIONALDATA_print(const char *contents, int BDFlag); -int U_PMF_PATHPOINTTYPE_print(const char *contents); -int U_PMF_PATHPOINTTYPERLE_print(const char *contents); -int U_PMF_PENDATA_print(const char *contents); -int U_PMF_PENOPTIONALDATA_print(const char *contents, int Flags); -int U_PMF_POINT_print(const char **contents); -int U_PMF_POINTF_print(const char **contents); -int U_PMF_POINTR_print(const char **contents, U_FLOAT *Xpos, U_FLOAT *Ypos); +int U_PMF_LINEARGRADIENTBRUSHDATA_print(const char *contents, const char *blimit); +int U_PMF_LINEARGRADIENTBRUSHOPTIONALDATA_print(const char *contents, int BDFlag, const char *blimit); +int U_PMF_LINEPATH_print(const char *contents, const char *blimit); +int U_PMF_METAFILE_print(const char *contents, const char *blimit); +int U_PMF_PALETTE_print(const char *contents, const char *blimit); +int U_PMF_PATHGRADIENTBRUSHDATA_print(const char *contents, const char *blimit); +int U_PMF_PATHGRADIENTBRUSHOPTIONALDATA_print(const char *contents, int BDFlag, const char *blimit); +int U_PMF_PATHPOINTTYPE_print(const char *contents, const char *blimit); +int U_PMF_PATHPOINTTYPERLE_print(const char *contents, const char *blimit); +int U_PMF_PENDATA_print(const char *contents, const char *blimit); +int U_PMF_PENOPTIONALDATA_print(const char *contents, int Flags, const char *blimit); +int U_PMF_POINT_print(const char **contents, const char *blimit); +int U_PMF_POINTF_print(const char **contents, const char *blimit); +int U_PMF_POINTR_print(const char **contents, U_FLOAT *Xpos, U_FLOAT *Ypos, const char *blimit); int U_PMF_POINT_S_print(U_PMF_POINT *Point); int U_PMF_POINTF_S_print(U_PMF_POINTF *Point); -int U_PMF_RECT_print(const char **contents); -int U_PMF_RECTF_print(const char **contents); +int U_PMF_RECT_print(const char **contents, const char *blimit); +int U_PMF_RECTF_print(const char **contents, const char *blimit); int U_PMF_RECT_S_print(U_PMF_RECT *Rect); int U_PMF_RECTF_S_print(U_PMF_RECTF *Rect); -int U_PMF_REGIONNODE_print(const char *contents, int Level); -int U_PMF_REGIONNODECHILDNODES_print(const char *contents, int Level); -int U_PMF_REGIONNODEPATH_print(const char *contents); -int U_PMF_SOLIDBRUSHDATA_print(const char *contents); -int U_PMF_STRINGFORMATDATA_print(const char *contents, uint32_t TabStopCount, uint32_t RangeCount); -int U_PMF_TEXTUREBRUSHDATA_print(const char *contents); -int U_PMF_TEXTUREBRUSHOPTIONALDATA_print(const char *contents, int HasImage); -int U_PMF_TRANSFORMMATRIX_print(const char *contents); +int U_PMF_REGIONNODE_print(const char *contents, int Level, const char *blimit); +int U_PMF_REGIONNODECHILDNODES_print(const char *contents, int Level, const char *blimit); +int U_PMF_REGIONNODEPATH_print(const char *contents, const char *blimit); +int U_PMF_SOLIDBRUSHDATA_print(const char *contents, const char *blimit); +int U_PMF_STRINGFORMATDATA_print(const char *contents, uint32_t TabStopCount, uint32_t RangeCount, const char *blimit); +int U_PMF_TEXTUREBRUSHDATA_print(const char *contents, const char *blimit); +int U_PMF_TEXTUREBRUSHOPTIONALDATA_print(const char *contents, int HasMatrix, int HasImage, const char *blimit); +int U_PMF_TRANSFORMMATRIX_print(const char *contents, const char *blimit); int U_PMF_TRANSFORMMATRIX2_print(U_PMF_TRANSFORMMATRIX *Matrix); int U_PMF_ROTMATRIX2_print(U_PMF_ROTMATRIX *Matrix); -int U_PMF_IE_BLUR_print(const char *contents); -int U_PMF_IE_BRIGHTNESSCONTRAST_print(const char *contents); -int U_PMF_IE_COLORBALANCE_print(const char *contents); -int U_PMF_IE_COLORCURVE_print(const char *contents); -int U_PMF_IE_COLORLOOKUPTABLE_print(const char *contents); -int U_PMF_IE_COLORMATRIX_print(const char *contents); -int U_PMF_IE_HUESATURATIONLIGHTNESS_print(const char *contents); -int U_PMF_IE_LEVELS_print(const char *contents); -int U_PMF_IE_REDEYECORRECTION_print(const char *contents); -int U_PMF_IE_SHARPEN_print(const char *contents); -int U_PMF_IE_TINT_print(const char *contents); +int U_PMF_IE_BLUR_print(const char *contents, const char *blimit); +int U_PMF_IE_BRIGHTNESSCONTRAST_print(const char *contents, const char *blimit); +int U_PMF_IE_COLORBALANCE_print(const char *contents, const char *blimit); +int U_PMF_IE_COLORCURVE_print(const char *contents, const char *blimit); +int U_PMF_IE_COLORLOOKUPTABLE_print(const char *contents, const char *blimit); +int U_PMF_IE_COLORMATRIX_print(const char *contents, const char *blimit); +int U_PMF_IE_HUESATURATIONLIGHTNESS_print(const char *contents, const char *blimit); +int U_PMF_IE_LEVELS_print(const char *contents, const char *blimit); +int U_PMF_IE_REDEYECORRECTION_print(const char *contents, const char *blimit); +int U_PMF_IE_SHARPEN_print(const char *contents, const char *blimit); +int U_PMF_IE_TINT_print(const char *contents, const char *blimit); /* prototypes for PMR records */ int U_PMR_OFFSETCLIP_print(const char *contents); @@ -127,14 +128,14 @@ int U_PMR_DRAWIMAGEPOINTS_print(const char *contents); int U_PMR_DRAWLINES_print(const char *contents); int U_PMR_DRAWPATH_print(const char *contents); int U_PMR_DRAWPIE_print(const char *contents); -int U_PMR_DRAWRECTS_print(const char *contents, const char *blimit); +int U_PMR_DRAWRECTS_print(const char *contents); int U_PMR_DRAWSTRING_print(const char *contents); int U_PMR_FILLCLOSEDCURVE_print(const char *contents); int U_PMR_FILLELLIPSE_print(const char *contents); int U_PMR_FILLPATH_print(const char *contents); int U_PMR_FILLPIE_print(const char *contents); int U_PMR_FILLPOLYGON_print(const char *contents); -int U_PMR_FILLRECTS_print(const char *contents, const char *blimit); +int U_PMR_FILLRECTS_print(const char *contents); int U_PMR_FILLREGION_print(const char *contents); int U_PMR_OBJECT_print(const char *contents, const char *blimit, U_OBJ_ACCUM *ObjCont, int term); int U_PMR_SERIALIZABLEOBJECT_print(const char *contents); diff --git a/src/libuemf/uwmf.c b/src/libuemf/uwmf.c index b378e587f..62e3d3c06 100644 --- a/src/libuemf/uwmf.c +++ b/src/libuemf/uwmf.c @@ -19,8 +19,8 @@ /* File: uwmf.c -Version: 0.0.15 -Date: 11-APR-2014 +Version: 0.0.17 +Date: 28-MAR-2015 Author: David Mathog, Biology Division, Caltech email: mathog@caltech.edu Copyright: 2014 David Mathog and California Institute of Technology (Caltech) @@ -47,6 +47,7 @@ extern "C" { #endif #include "uwmf.h" #include "uwmf_endian.h" +#include "uemf_safe.h" /** \brief Look up the full numeric type of a WMR record by type. @@ -671,6 +672,52 @@ const char *U_wmr_escnames(int idx){ /* one prototype from uwmf_endian. Put it here because end user should never need to see it, so not in uemf.h or uwmf_endian.h */ void U_swap2(void *ul, unsigned int count); + +/** + \brief Check that the bitmap in the specified packed DIB is compatible with the record size + + \return 1 on success, 0 on failure + \param record EMF record that contains a DIB pixel array + \param blimit one byte past the end of the record. + + This method can only test DIBs that hold Microsoft's various bitmap types. PNG or JPG is just a bag + of bytes, and there is no possible way to derive from the known width and height how big it should be. + + This should not be called directly by end user code. +*/ +int packed_DIB_safe( + const char *record, + const char *blimit + ){ + int dibparams = U_BI_UNKNOWN; // type of image not yet determined + const char *px = NULL; // DIB pixels + const U_RGBQUAD *ct = NULL; // DIB color table + int bs; + int usedbytes; + + if(!bitmapinfo_safe(record, blimit))return(0); // this DIB has issues with colors fitting into the record + uint32_t numCt; // these values will be set in get_DIB_params + int32_t width, height, colortype, invert; // these values will be set in get_DIB_params + // next call returns pointers and values, but allocates no memory + dibparams = wget_DIB_params(record, &px, (const U_RGBQUAD **) &ct, &numCt, &width, &height, &colortype, &invert); + // sanity checking + if(numCt && colortype >= U_BCBM_COLOR16)return(0); //color tables not used above 16 bit pixels + if(!numCt && colortype < U_BCBM_COLOR16)return(0); //color tables mandatory for < 16 bit + + if(dibparams ==U_BI_RGB){ + // this is the only DIB type where we can calculate how big it should be when stored in the WMF file + bs = colortype/8; + if(bs<1){ + usedbytes = (width*colortype + 7)/8; // width of line in fully and partially occupied bytes + } + else { + usedbytes = width*bs; + } + if(IS_MEM_UNSAFE(px, usedbytes, blimit))return(0); + } + return(1); +} + //! \endcond /** @@ -1518,7 +1565,7 @@ int wmf_finish( #if U_BYTE_SWAP //This is a Big Endian machine, WMF data must be Little Endian - U_wmf_endian(wt->buf,wt->used,1); + U_wmf_endian(wt->buf,wt->used,1,0); // BE to LE, entire file #endif (void) U_wmr_properties(U_WMR_INVALID); /* force the release of the lookup table memory, returned value is irrelevant */ @@ -1564,7 +1611,7 @@ int wmf_readdata( else { #if U_BYTE_SWAP //This is a Big Endian machine, WMF data is Little Endian - U_wmf_endian(*contents,*length,0); // LE to BE + U_wmf_endian(*contents,*length,0,0); // LE to BE, entire file #endif } } @@ -4439,9 +4486,7 @@ size_t U_WMRRECSAFE_get( memcpy(&Size16, contents + offsetof(U_METARECORD,Size16_4), 4); size = 2*Size16; /* Record is not self consistent - described size past the end of WMF in memory */ - if(size < U_SIZE_METARECORD || - contents + size - 1 >= blimit || - contents + size - 1 < contents)size=0; + if(size < U_SIZE_METARECORD || IS_MEM_UNSAFE(contents, size, blimit))size=0; return(size); } @@ -4676,10 +4721,10 @@ int U_WMRCORE_PALETTE_get( */ void U_BITMAPCOREHEADER_get( const char *BmiCh, - int32_t *Size, - int32_t *Width, - int32_t *Height, - int32_t *BitCount + uint32_t *Size, + int32_t *Width, + int32_t *Height, + int32_t *BitCount ){ uint32_t utmp4; uint16_t utmp2; @@ -4756,14 +4801,14 @@ int wget_DIB_params( const char *dib, const char **px, const U_RGBQUAD **ct, - int32_t *numCt, - int32_t *width, - int32_t *height, - int32_t *colortype, - int32_t *invert + uint32_t *numCt, + int32_t *width, + int32_t *height, + int32_t *colortype, + int32_t *invert ){ uint32_t bic; - int32_t Size; + uint32_t Size; bic = U_BI_RGB; // this information is not in the coreheader; U_BITMAPCOREHEADER_get(dib, &Size, width, height, colortype); if(Size != 0xC ){ //BitmapCoreHeader @@ -4772,7 +4817,7 @@ int wget_DIB_params( */ uint32_t uig4; int32_t ig4; - U_BITMAPINFOHEADER_get(dib, &uig4, width, height,&uig4, (uint32_t *) colortype, &bic, &uig4, &ig4, &ig4,&uig4, &uig4); + U_BITMAPINFOHEADER_get(dib, &uig4, width, height,&uig4, (uint32_t *) colortype, &bic, &uig4, &ig4, &ig4, &uig4, &uig4); } if(*height < 0){ *height = -*height; @@ -4826,20 +4871,21 @@ int wmfheader_get( ){ uint32_t Key; int size=0; - if(!Placeable || !Header || contents + 4 >= blimit)return(0); + if(!contents || !Placeable || !Header || !blimit)return(0); + if(IS_MEM_UNSAFE(contents, 4, blimit))return(0); memcpy(&Key, contents + offsetof(U_WMRPLACEABLE,Key), 4); if(Key == 0x9AC6CDD7){ size += U_SIZE_WMRPLACEABLE; - if(contents + size >= blimit)return(0); + if(IS_MEM_UNSAFE(contents, size, blimit))return(0); memcpy(Placeable, contents, U_SIZE_WMRPLACEABLE); contents += U_SIZE_WMRPLACEABLE; } else { memset(Placeable, 0, U_SIZE_WMRPLACEABLE); } - if(contents + size + U_SIZE_WMRHEADER >= blimit)return(0); + if(IS_MEM_UNSAFE(contents, size + U_SIZE_WMRHEADER, blimit))return(0); size += 2* (*(uint16_t *)(contents + offsetof(U_WMRHEADER,Size16w))); - if(contents + size >= blimit)return(0); + if(IS_MEM_UNSAFE(contents, size, blimit))return(0); memcpy(Header, contents, U_SIZE_WMRHEADER); return(size); } @@ -5512,7 +5558,12 @@ int U_WMRPOLYGON_get( uint16_t *Length, const char **Data ){ - return U_WMRCORE_2U16_N16_get(contents, (U_SIZE_WMRPOLYGON), NULL, Length, Data); + int size = U_WMRCORE_2U16_N16_get(contents, (U_SIZE_WMRPOLYGON), NULL, Length, Data); + if(size){ + int iLength = (*Length)*sizeof(U_POINT16); + if(IS_MEM_UNSAFE(*Data, iLength, contents+size))return(0); + } + return size; } /** @@ -5527,7 +5578,12 @@ int U_WMRPOLYLINE_get( uint16_t *Length, const char **Data ){ - return U_WMRCORE_2U16_N16_get(contents, (U_SIZE_WMRPOLYLINE), NULL, Length, Data); + int size = U_WMRCORE_2U16_N16_get(contents, (U_SIZE_WMRPOLYGON), NULL, Length, Data); + if(size){ + int iLength = (*Length)*sizeof(U_POINT16); + if(IS_MEM_UNSAFE(*Data, iLength, contents+size))return(0); + } + return size; } /** @@ -5550,7 +5606,12 @@ int U_WMRESCAPE_get( uint16_t *Length, const char **Data ){ - return U_WMRCORE_2U16_N16_get(contents, (U_SIZE_WMRESCAPE), Escape, Length, Data); + int size = U_WMRCORE_2U16_N16_get(contents, (U_SIZE_WMRESCAPE), Escape, Length, Data); + if(size){ + int iLength=*Length; + if(IS_MEM_UNSAFE(*Data, iLength, contents+size))return(0); + } + return size; } /** @@ -5787,6 +5848,7 @@ int U_WMRSETDIBTODEV_get( Dst->y = *(int16_t *)( contents + offsetof(U_WMRSETDIBTODEV, yDst )); Dst->x = *(int16_t *)( contents + offsetof(U_WMRSETDIBTODEV, xDst )); *dib = ( contents + offsetof(U_WMRSETDIBTODEV, dib )); + if(!packed_DIB_safe(*dib, *dib+size))return(0); return(size); } @@ -5948,6 +6010,7 @@ int U_WMRDIBBITBLT_get( Dst->y = *(int16_t *)( contents + offsetof(U_WMRDIBBITBLT_PX, yDst )); Dst->x = *(int16_t *)( contents + offsetof(U_WMRDIBBITBLT_PX, xDst )); *dib = ( contents + offsetof(U_WMRDIBBITBLT_PX, dib )); + if(!packed_DIB_safe(*dib, *dib+size))return(0); } return(size); } @@ -5999,6 +6062,7 @@ int U_WMRDIBSTRETCHBLT_get( cDst->y = *(int16_t *)( contents + offsetof(U_WMRDIBSTRETCHBLT_PX, hDst )); cDst->x = *(int16_t *)( contents + offsetof(U_WMRDIBSTRETCHBLT_PX, wDst )); *dib = ( contents + offsetof(U_WMRDIBSTRETCHBLT_PX, dib )); + if(!packed_DIB_safe(*dib, *dib+size))return(0); } return(size); } @@ -6040,11 +6104,13 @@ int U_WMRDIBCREATEPATTERNBRUSH_get( if(TmpBm16.Width <= 0 || TmpBm16.Height <= 0 || TmpBm16.Planes != 1 || TmpBm16.BitsPixel == 0){ *Bm16 = NULL; *dib = (contents + offsetof(U_WMRDIBCREATEPATTERNBRUSH, Src)); + if(!packed_DIB_safe(*dib, *dib+size))return(0); } } else { /* from DIB */ *Bm16 = NULL; *dib = (contents + offsetof(U_WMRDIBCREATEPATTERNBRUSH, Src)); + if(!packed_DIB_safe(*dib, *dib+size))return(0); } return(size); } @@ -6085,6 +6151,7 @@ int U_WMRSTRETCHDIB_get( Dst->y = *(int16_t *)( contents + offsetof(U_WMRSTRETCHDIB, yDst )); Dst->x = *(int16_t *)( contents + offsetof(U_WMRSTRETCHDIB, xDst )); *dib = ( contents + offsetof(U_WMRSTRETCHDIB, dib )); + if(!packed_DIB_safe(*dib, *dib+size))return(0); return(size); } @@ -6914,7 +6981,12 @@ int U_WMRCREATEFONTINDIRECT_get( const char *contents, const char **font ){ - return U_WMRCORE_2U16_N16_get(contents, (U_SIZE_WMRCREATEFONTINDIRECT), NULL, NULL, font); + int size = U_WMRCORE_2U16_N16_get(contents, (U_SIZE_WMRCREATEFONTINDIRECT), NULL, NULL, font); + if(size){ + if(IS_MEM_UNSAFE(*font, U_SIZE_FONT_CORE, contents+size))return(0); + if(contents + size - *font > U_SIZE_FONT_CORE + 32)return(0); // font name must fit in a 32 bit field + } + return size; } /** @@ -6927,6 +6999,7 @@ int U_WMRCREATEBRUSHINDIRECT_get( const char *contents, const char **brush ){ + // U_SIZE_WMRCREATEBRUSHINDIRECT is everything, no variable part, so the test below is sufficient return U_WMRCORE_2U16_N16_get(contents, (U_SIZE_WMRCREATEBRUSHINDIRECT), NULL, NULL, brush); } @@ -6947,16 +7020,19 @@ int U_WMRCREATEBITMAP_get(void){ \return length of the U_WMRCREATEREGION record in bytes, or 0 on error \param contents record to extract data from \param Region pointer to U_REGION structure in memory. Pointer may not be aligned properly for structure. + + Caller must check at the returned Region does not extend outside of the record! */ int U_WMRCREATEREGION_get( const char *contents, const char **Region ){ - return U_WMRCORE_2U16_N16_get(contents, (U_SIZE_WMRCREATEREGION), NULL, NULL, Region); + return U_WMRCORE_2U16_N16_get(contents, (U_SIZE_WMRCREATEREGION), NULL, NULL, Region); } + #ifdef __cplusplus } #endif diff --git a/src/libuemf/uwmf.h b/src/libuemf/uwmf.h index 54f7f36fe..027de8e06 100644 --- a/src/libuemf/uwmf.h +++ b/src/libuemf/uwmf.h @@ -36,11 +36,11 @@ /* File: uwmf.h -Version: 0.0.10 -Date: 24-MAR-2014 +Version: 0.0.12 +Date: 28-APR-2015 Author: David Mathog, Biology Division, Caltech email: mathog@caltech.edu -Copyright: 2014 David Mathog and California Institute of Technology (Caltech) +Copyright: 2015 David Mathog and California Institute of Technology (Caltech) */ #ifndef _UWMF_ @@ -728,6 +728,7 @@ enum U_WMR_TYPES{ #define U_SIZE_WMRCREATEFONTINDIRECT 26 /**< X 28 */ #define U_SIZE_WMRCREATEBRUSHINDIRECT 14 /**< X 16 */ #define U_SIZE_WMRCREATEREGION 26 /**< X 28 */ +#define U_SIZE_WMRCREATEREGION_CORE 24 /**< X 28 Like U_SIZE_WMRCREATEREGION minus the variable part */ /** @} */ @@ -2406,8 +2407,8 @@ int wmr_arc_points(U_RECT16 rclBox, U_POINT16 ArcStart, U_POINT16 ArcEn void U_BITMAPINFOHEADER_get(const char *Bmih, uint32_t *Size, int32_t *Width, int32_t *Height, uint32_t *Planes, uint32_t *BitCount, uint32_t *Compression, uint32_t *SizeImage, int32_t *XPelsPerMeter, int32_t *YPelsPerMeter, uint32_t *ClrUsed, uint32_t *ClrImportant); -void U_BITMAPCOREHEADER_get(const char *BmiCh, int32_t *Size, int32_t *Width, int32_t *Height, int32_t *BitCount); -int wget_DIB_params(const char *dib, const char **px, const U_RGBQUAD **ct, int32_t *numCt, +void U_BITMAPCOREHEADER_get(const char *BmiCh, uint32_t *Size, int32_t *Width, int32_t *Height, int32_t *BitCount); +int wget_DIB_params(const char *dib, const char **px, const U_RGBQUAD **ct, uint32_t *numCt, int32_t *width, int32_t *height, int32_t *colortype, int32_t *invert); int U_WMREOF_get(const char *contents); int U_WMRSETBKCOLOR_get(const char *contents, U_COLORREF *Color); diff --git a/src/libuemf/uwmf_endian.c b/src/libuemf/uwmf_endian.c index 38a321ad0..7a047c2a5 100644 --- a/src/libuemf/uwmf_endian.c +++ b/src/libuemf/uwmf_endian.c @@ -6,11 +6,11 @@ /* File: uwmf_endian.c -Version: 0.1.3 -Date: 27-MAR-2014 +Version: 0.1.5 +Date: 28-MAY-2015 Author: David Mathog, Biology Division, Caltech email: mathog@caltech.edu -Copyright: 2014 David Mathog and California Institute of Technology (Caltech) +Copyright: 2015 David Mathog and California Institute of Technology (Caltech) */ #ifdef __cplusplus @@ -1470,21 +1470,25 @@ void U_WMRCREATEREGION_swap(char *record, int torev){ \param contents pointer to the buffer holding the entire EMF in memory \param length number of bytes in the buffer \param torev 1 for native to reversed, 0 for reversed to native + \param onerec 1 if this is operating on a single record instead of an entire file Normally this would be called immediately before writing the data to a file or immediately after reading the data from a file. */ -int U_wmf_endian(char *contents, size_t length, int torev){ - size_t off; +int U_wmf_endian(char *contents, size_t length, int torev, int onerec){ + size_t off=0; uint32_t OK, Size16; uint8_t iType; char *record; - int recnum, offset; + int recnum; + int offset=0; record = contents; - off = wmfheader_swap(record,torev); fflush(stdout); /* WMF header is not a normal record, handle it separately */ - record += off; - offset = off; + if(!onerec){ + off = wmfheader_swap(record,torev); fflush(stdout); /* WMF header is not a normal record, handle it separately */ + record += off; + offset = off; + } OK = 1; recnum = 1; /* used when debugging */ @@ -1756,6 +1760,7 @@ int U_wmf_endian(char *contents, size_t length, int torev){ case U_WMR_CREATEREGION: U_WMRCREATEREGION_swap(record, torev); break; default: U_WMRNOTIMPLEMENTED_swap(record, torev); break; } //end of switch + if(onerec)break; record += 2*Size16; offset += 2*Size16; recnum++; diff --git a/src/libuemf/uwmf_endian.h b/src/libuemf/uwmf_endian.h index 57fd4ae44..6ce7f1984 100644 --- a/src/libuemf/uwmf_endian.h +++ b/src/libuemf/uwmf_endian.h @@ -6,11 +6,11 @@ /* File: uwmf_endian.h -Version: 0.0.2 -Date: 26-NOV-2013 +Version: 0.0.3 +Date: 28-APR-2015 Author: David Mathog, Biology Division, Caltech email: mathog@caltech.edu -Copyright: 2013 David Mathog and California Institute of Technology (Caltech) +Copyright: 2015 David Mathog and California Institute of Technology (Caltech) */ #ifndef _UWMF_ENDIAN_ @@ -24,7 +24,7 @@ extern "C" { //! \cond // prototypes -int U_wmf_endian(char *contents, size_t length, int torev); +int U_wmf_endian(char *contents, size_t length, int torev, int onerec); //! \endcond #ifdef __cplusplus diff --git a/src/libuemf/uwmf_print.c b/src/libuemf/uwmf_print.c index 0e089a5cd..d6d1b584e 100644 --- a/src/libuemf/uwmf_print.c +++ b/src/libuemf/uwmf_print.c @@ -6,11 +6,11 @@ /* File: uwmf_print.c -Version: 0.0.4 -Date: 05-FEB-2014 +Version: 0.0.6 +Date: 21-MAY-2015 Author: David Mathog, Biology Division, Caltech email: mathog@caltech.edu -Copyright: 2014 David Mathog and California Institute of Technology (Caltech) +Copyright: 2015 David Mathog and California Institute of Technology (Caltech) */ #ifdef __cplusplus @@ -319,7 +319,7 @@ void scan_print( \param dh void pointer to DIB header A DIB header in an WMF may be either a BitmapCoreHeader or BitmapInfoHeader. */ -void dibheader_print(const void *dh){ +void dibheader_print(const char *dh, const char *blimit){ uint32_t Size; memcpy(&Size, dh, 4); /* may not be aligned */ if(Size == 0xC ){ @@ -330,7 +330,7 @@ void dibheader_print(const void *dh){ } else { printf(" (BitmapInfoHeader) "); - bitmapinfo_print(dh); /* may not be aligned, called routine must handle it */ + bitmapinfo_print(dh, blimit); /* may not be aligned, called routine must handle it */ } } @@ -922,7 +922,7 @@ void U_WMRSETDIBTODEV_print(const char *contents){ printf(" Src X,Y:{%d,%d}\n", Src.x, Src.y ); printf(" W,H:%d,%d\n", cwh.x, cwh.y ); printf(" Dst X,Y:{%d,%d}\n", Dst.x, Dst.y ); - printf(" DIB:"); dibheader_print(dib); printf("\n"); + printf(" DIB:"); dibheader_print(dib, dib+size); printf("\n"); } } @@ -993,7 +993,7 @@ void U_WMRDIBBITBLT_print(const char *contents){ printf(" Src X,Y:{%d,%d}\n", Src.x, Src.x ); printf(" W,H:%d,%d\n", cwh.x, cwh.y ); printf(" Dst X,Y:{%d,%d}\n", Dst.x, Dst.y ); - if(dib){ printf(" DIB:"); dibheader_print(dib); printf("\n"); } + if(dib){ printf(" DIB:"); dibheader_print(dib, dib+size); printf("\n"); } else { printf(" DIB: none\n"); } } } @@ -1009,7 +1009,7 @@ void U_WMRDIBSTRETCHBLT_print(const char *contents){ printf(" Src X,Y:{%d,%d}\n", Src.x, Src.x ); printf(" Dst W,H:%d,%d\n", cDst.x, cDst.y ); printf(" Dst X,Y:{%d,%d}\n", Dst.x, Dst.y ); - if(dib){ printf(" DIB:"); dibheader_print(dib); printf("\n"); } + if(dib){ printf(" DIB:"); dibheader_print(dib, dib+size); printf("\n"); } else { printf(" DIB: none\n"); } } } @@ -1028,7 +1028,7 @@ void U_WMRDIBCREATEPATTERNBRUSH_print(const char *contents){ printf(" Src:Bitmap16:"); bitmap16_print(Bm16); printf("\n"); } else { /* from DIB */ - printf(" Src:DIB:"); dibheader_print(dib); printf("\n"); + printf(" Src:DIB:"); dibheader_print(dib, dib+size); printf("\n"); } } } @@ -1046,7 +1046,7 @@ void U_WMRSTRETCHDIB_print(const char *contents){ printf(" Src X,Y:{%d,%d}\n", Src.x, Src.x ); printf(" Dst W,H:%d,%d\n", cDst.x, cDst.y ); printf(" Dst X,Y:{%d,%d}\n", Dst.x, Dst.y ); - if(dib){ printf(" DIB:"); dibheader_print(dib); printf("\n"); } + if(dib){ printf(" DIB:"); dibheader_print(dib, dib+size); printf("\n"); } else { printf(" DIB: none\n"); } } } @@ -1351,13 +1351,20 @@ int U_wmf_onerec_print(const char *contents, const char *blimit, int recnum, siz iType = *(uint8_t *)(contents + offsetof(U_METARECORD, iType ) ); -#if 1 - printf("%-30srecord:%5d type:%-4u offset:%8d rsize:%8u\n", - U_wmr_names(iType), recnum, iType, (int) off, (int) size); -#else /* show record checksums, this is NOT portable, result changes with endian type, useful for debugging */ - printf("%-30srecord:%5d type:%-4u offset:%8d size:%8u recchecksum:%u\n", - U_wmr_names(iType), recnum, iType, (int) off, (int) size, U_16_checksum((int16_t *)contents, size)); + uint32_t crc; +#if U_BYTE_SWAP + //This is a Big Endian machine, WMF crc values must be calculated on Little Endian form + char *swapbuf=malloc(size); + if(!swapbuf)return(-1); + memcpy(swapbuf,contents,size); + U_wmf_endian(swapbuf,size,1,1); // BE to LE + crc=lu_crc32(swapbuf,size); + free(swapbuf); +#else + crc=lu_crc32(contents,size); #endif + printf("%-30srecord:%5d type:%-4u offset:%8d rsize:%8u crc32:%8.8X\n", + U_wmr_names(iType), recnum, iType, (int) off, (int) size, crc); switch (iType) { diff --git a/src/libuemf/uwmf_print.h b/src/libuemf/uwmf_print.h index 8daee0feb..54dfe9c9c 100644 --- a/src/libuemf/uwmf_print.h +++ b/src/libuemf/uwmf_print.h @@ -37,7 +37,7 @@ void bitmapcoreheader_print(U_BITMAPCOREHEADER ch); void logbrushw_print(U_WLOGBRUSH lb); void polypolygon_print(uint16_t nPolys, const uint16_t *aPolyCounts, const char *Points); void scan_print(U_SCAN sc); -void dibheader_print(const void *dh); +void dibheader_print(const char *dh, const char *blimit); /* prototypes for WMF records */ int wmfheader_print(const char *contents, const char *blimit); diff --git a/src/livarot/PathConversion.cpp b/src/livarot/PathConversion.cpp index 42df898e6..30e21d546 100644 --- a/src/livarot/PathConversion.cpp +++ b/src/livarot/PathConversion.cpp @@ -716,7 +716,7 @@ static void ArcAnglesAndCenter(Geom::Point const &iS, Geom::Point const &iE, { Geom::Point se = iE - iS; Geom::Point ca(cos(angle), sin(angle)); - Geom::Point cse(dot(se, ca), cross(se, ca)); + Geom::Point cse(dot(ca, se), cross(ca, se)); cse[0] /= rx; cse[1] /= ry; double const lensq = dot(cse,cse); @@ -753,8 +753,8 @@ static void ArcAnglesAndCenter(Geom::Point const &iS, Geom::Point const &iE, csd[1] *= ry; ca[1] = -ca[1]; // because it's the inverse rotation - dr[0] = dot(csd, ca); - dr[1] = cross(csd, ca); + dr[0] = dot(ca, csd); + dr[1] = cross(ca, csd); ca[1] = -ca[1]; diff --git a/src/livarot/PathCutting.cpp b/src/livarot/PathCutting.cpp index 49b2c5a78..086b30557 100644 --- a/src/livarot/PathCutting.cpp +++ b/src/livarot/PathCutting.cpp @@ -308,7 +308,7 @@ Path::MakePathVector() { /* TODO: add testcase for this descr_arcto case */ PathDescrArcTo *nData = dynamic_cast<PathDescrArcTo *>(descr_cmd[i]); - currentpath->appendNew<Geom::SVGEllipticalArc>( nData->rx, nData->ry, nData->angle*M_PI/180.0, nData->large, !nData->clockwise, nData->p ); + currentpath->appendNew<Geom::EllipticalArc>( nData->rx, nData->ry, nData->angle*M_PI/180.0, nData->large, !nData->clockwise, nData->p ); lastP = nData->p; } break; @@ -400,11 +400,11 @@ void Path::AddCurve(Geom::Curve const &c) Geom::Point tme = 3 * ((*cubic_bezier)[3] - (*cubic_bezier)[2]); CubicTo (tmp, tms, tme); } - else if(Geom::SVGEllipticalArc const *svg_elliptical_arc = dynamic_cast<Geom::SVGEllipticalArc const *>(&c)) { - ArcTo( svg_elliptical_arc->finalPoint(), - svg_elliptical_arc->ray(Geom::X), svg_elliptical_arc->ray(Geom::Y), - svg_elliptical_arc->rotationAngle()*180.0/M_PI, // convert from radians to degrees - svg_elliptical_arc->largeArc(), !svg_elliptical_arc->sweep() ); + else if(Geom::EllipticalArc const *elliptical_arc = dynamic_cast<Geom::EllipticalArc const *>(&c)) { + ArcTo( elliptical_arc->finalPoint(), + elliptical_arc->ray(Geom::X), elliptical_arc->ray(Geom::Y), + elliptical_arc->rotationAngle()*180.0/M_PI, // convert from radians to degrees + elliptical_arc->largeArc(), !elliptical_arc->sweep() ); } else { //this case handles sbasis as well as all other curve types Geom::Path sbasis_path = Geom::cubicbezierpath_from_sbasis(c.toSBasis(), 0.1); @@ -434,17 +434,11 @@ void Path::LoadPath(Geom::Path const &path, Geom::Affine const &tr, bool doTran MoveTo( pathtr.initialPoint() ); - for(Geom::Path::const_iterator cit = pathtr.begin(); cit != pathtr.end_open(); ++cit) { + for(Geom::Path::const_iterator cit = pathtr.begin(); cit != pathtr.end(); ++cit) { AddCurve(*cit); } if (pathtr.closed()) { - // check if closing segment is empty before adding it - Geom::Curve const &crv = pathtr.back_closed(); - if ( !crv.isDegenerate() ) { - AddCurve(crv); - } - Close(); } } @@ -513,10 +507,10 @@ double Path::Surface() for (std::vector<path_lineto>::const_iterator i = pts.begin(); i != pts.end(); ++i) { if ( i->isMoveTo == polyline_moveto ) { - surf += Geom::cross(lastM - lastP, lastM); + surf += Geom::cross(lastM, lastM - lastP); lastP = lastM = i->p; } else { - surf += Geom::cross(i->p - lastP, i->p); + surf += Geom::cross(i->p, i->p - lastP); lastP = i->p; } diff --git a/src/livarot/PathOutline.cpp b/src/livarot/PathOutline.cpp index 211ee31e2..e146bb908 100644 --- a/src/livarot/PathOutline.cpp +++ b/src/livarot/PathOutline.cpp @@ -1108,7 +1108,7 @@ Path::TangentOnCubAt (double at, Geom::Point const &iS, PathDescrCubicTo const & } return; } - rad = -l * (dot(dder,dder)) / (cross(ddder,dder)); + rad = -l * (dot(dder,dder)) / (cross(dder, ddder)); tgt = dder / l; if (before) { tgt = -tgt; @@ -1117,7 +1117,7 @@ Path::TangentOnCubAt (double at, Geom::Point const &iS, PathDescrCubicTo const & } len = l; - rad = -l * (dot(der,der)) / (cross(dder,der)); + rad = -l * (dot(der,der)) / (cross(der, dder)); tgt = der / l; } @@ -1156,7 +1156,7 @@ Path::TangentOnBezAt (double at, Geom::Point const &iS, return; } len = l; - rad = -l * (dot(der,der)) / (cross(dder,der)); + rad = -l * (dot(der,der)) / (cross(der, dder)); tgt = der / l; } @@ -1180,7 +1180,7 @@ Path::OutlineJoin (Path * dest, Geom::Point pos, Geom::Point stNor, Geom::Point TurnInside ^= PrevPos == pos; PrevPos = pos; - const double angSi = cross (enNor,stNor); + const double angSi = cross (stNor, enNor); const double angCo = dot (stNor, enNor); if ((fabs(angSi) < .0000001) && angCo > 0) { // The join is straight -> nothing to do. diff --git a/src/livarot/PathSimplify.cpp b/src/livarot/PathSimplify.cpp index 7d9f3f57d..81ddcd049 100644 --- a/src/livarot/PathSimplify.cpp +++ b/src/livarot/PathSimplify.cpp @@ -128,7 +128,7 @@ static double DistanceToCubic(Geom::Point const &start, PathDescrCubicTo res, Ge } Geom::Point seg = res.p - start; - nnle = Geom::cross(seg, sp); + nnle = Geom::cross(sp, seg); nnle *= nnle; nnle /= Geom::dot(seg, seg); if ( nnle < nle ) { diff --git a/src/livarot/PathStroke.cpp b/src/livarot/PathStroke.cpp index 6ec7fa209..4cfeb887a 100644 --- a/src/livarot/PathStroke.cpp +++ b/src/livarot/PathStroke.cpp @@ -292,7 +292,7 @@ void Path::DoJoin (Shape *dest, double width, JoinType join, Geom::Point pos, Ge { Geom::Point pnor = prev.ccw(); Geom::Point nnor = next.ccw(); - double angSi = cross(next, prev); + double angSi = cross(prev, next); /* FIXED: this special case caused bug 1028953 */ if (angSi > -0.0001 && angSi < 0.0001) { @@ -416,7 +416,7 @@ Path::DoLeftJoin (Shape * dest, double width, JoinType join, Geom::Point pos, { Geom::Point pnor=prev.ccw(); Geom::Point nnor=next.ccw(); - double angSi = cross (next, prev); + double angSi = cross(prev, next); if (angSi > -0.0001 && angSi < 0.0001) { double angCo = dot (prev, next); @@ -444,7 +444,7 @@ Path::DoLeftJoin (Shape * dest, double width, JoinType join, Geom::Point pos, /* Geom::Point biss; biss.x=next.x-prev.x; biss.y=next.y-prev.y; - double c2=cross(biss,next); + double c2=cross(next, biss); double l=width/c2; double projn=l*(dot(biss,next)); double projp=-l*(dot(biss,prev)); @@ -503,7 +503,7 @@ Path::DoLeftJoin (Shape * dest, double width, JoinType join, Geom::Point pos, } else { - double s2 = cross (biss, nnor); + double s2 = cross(nnor, biss); double dec = (l - emiter) * c2 / s2; const Geom::Point tbiss=biss.ccw(); @@ -560,7 +560,7 @@ Path::DoRightJoin (Shape * dest, double width, JoinType join, Geom::Point pos, { const Geom::Point pnor=prev.ccw(); const Geom::Point nnor=next.ccw(); - double angSi = cross (next,prev); + double angSi = cross(prev, next); if (angSi > -0.0001 && angSi < 0.0001) { double angCo = dot (prev, next); @@ -614,7 +614,7 @@ Path::DoRightJoin (Shape * dest, double width, JoinType join, Geom::Point pos, } else { - double s2 = cross (biss, nnor); + double s2 = cross(nnor, biss); double dec = (l - emiter) * c2 / s2; const Geom::Point tbiss=biss.ccw(); @@ -667,7 +667,7 @@ Path::DoRightJoin (Shape * dest, double width, JoinType join, Geom::Point pos, /* Geom::Point biss; biss=next.x-prev.x; biss.y=next.y-prev.y; - double c2=cross(next,biss); + double c2=cross(biss, next); double l=width/c2; double projn=l*(dot(biss,next)); double projp=-l*(dot(biss,prev)); @@ -719,7 +719,7 @@ void Path::RecRound(Shape *dest, int sNo, int eNo, // start and end index sia = 1; } else { double coa = dot(nS, nE); - sia = cross(nS, nE); + sia = cross(nE, nS); ang = acos(coa); if ( coa >= 1 ) { ang = 0; diff --git a/src/livarot/Shape.cpp b/src/livarot/Shape.cpp index ac6c72342..0bb3390e8 100644 --- a/src/livarot/Shape.cpp +++ b/src/livarot/Shape.cpp @@ -1683,7 +1683,7 @@ Shape::CmpToVert (Geom::Point ax, Geom::Point bx,bool as,bool bs) Geom::Point av, bv; av = ax; bv = bx; - double si = cross (bv, av); + double si = cross(av, bv); int tstSi = 0; if (si > 0.000001) tstSi = 1; if (si < -0.000001) tstSi = -1; @@ -2104,7 +2104,7 @@ Shape::PtWinding (const Geom::Point px) const } Geom::Point const diff = px - ast; - double const cote = cross(diff, adir); + double const cote = cross(adir, diff); if (cote == 0) continue; if (cote < 0) { if (ast[0] > px[0]) lr += nWeight; diff --git a/src/livarot/ShapeSweep.cpp b/src/livarot/ShapeSweep.cpp index b04b36bfd..1e6273964 100644 --- a/src/livarot/ShapeSweep.cpp +++ b/src/livarot/ShapeSweep.cpp @@ -1738,7 +1738,7 @@ Shape::TesteIntersection (SweepTree * iL, SweepTree * iR, Geom::Point &atx, doub } } - double ang = cross (rdir, ldir); + double ang = cross (ldir, rdir); // ang*=iL->src->eData[iL->bord].isqlength; // ang*=iR->src->eData[iR->bord].isqlength; if (ang <= 0) return false; // edges in opposite directions: <-left ... right -> @@ -1776,12 +1776,12 @@ Shape::TesteIntersection (SweepTree * iL, SweepTree * iR, Geom::Point &atx, doub double srDot, erDot; sDiff = iL->src->pData[lSt].rx - iR->src->pData[rSt].rx; eDiff = iL->src->pData[lEn].rx - iR->src->pData[rSt].rx; - srDot = cross (sDiff,rdir); - erDot = cross (eDiff,rdir); + srDot = cross(rdir, sDiff); + erDot = cross(rdir, eDiff); sDiff = iR->src->pData[rSt].rx - iL->src->pData[lSt].rx; eDiff = iR->src->pData[rEn].rx - iL->src->pData[lSt].rx; - slDot = cross (sDiff,ldir); - elDot = cross (eDiff,ldir); + slDot = cross(ldir, sDiff); + elDot = cross(ldir, eDiff); if ((srDot >= 0 && erDot >= 0) || (srDot <= 0 && erDot <= 0)) { @@ -2089,7 +2089,7 @@ Shape::Winding (const Geom::Point px) const } diff = px - ast; - double cote = cross (diff,adir); + double cote = cross(adir, diff); if (cote == 0) continue; if (cote < 0) @@ -2563,15 +2563,15 @@ Shape::TesteIntersection (Shape * ils, Shape * irs, int ilb, int irb, double srDot, erDot; sDiff = ils->pData[lSt].rx - irs->pData[rSt].rx; eDiff = ils->pData[lEn].rx - irs->pData[rSt].rx; - srDot = cross (sDiff,rdir ); - erDot = cross (eDiff,rdir ); + srDot = cross(rdir, sDiff); + erDot = cross(rdir, eDiff); if ((srDot >= 0 && erDot >= 0) || (srDot <= 0 && erDot <= 0)) return false; sDiff = irs->pData[rSt].rx - ils->pData[lSt].rx; eDiff = irs->pData[rEn].rx - ils->pData[lSt].rx; - slDot = cross (sDiff,ldir ); - elDot = cross (eDiff,ldir); + slDot = cross(ldir, sDiff); + elDot = cross(ldir, eDiff); if ((slDot >= 0 && elDot >= 0) || (slDot <= 0 && elDot <= 0)) return false; @@ -2615,8 +2615,8 @@ Shape::TesteIntersection (Shape * ils, Shape * irs, int ilb, int irb, double sDot, eDot; sDiff = ils->pData[lSt].rx - irs->pData[rSt].rx; eDiff = ils->pData[lEn].rx - irs->pData[rSt].rx; - sDot = cross (sDiff,rdir ); - eDot = cross (eDiff,rdir); + sDot = cross(rdir, sDiff); + eDot = cross(rdir, eDiff); atx = (sDot * irs->pData[lEn].rx - eDot * irs->pData[lSt].rx) / (sDot - @@ -2625,8 +2625,8 @@ Shape::TesteIntersection (Shape * ils, Shape * irs, int ilb, int irb, sDiff = irs->pData[rSt].rx - ils->pData[lSt].rx; eDiff = irs->pData[rEn].rx - ils->pData[lSt].rx; - sDot = cross (sDiff,ldir ); - eDot = cross (eDiff,ldir ); + sDot = cross(ldir, sDiff); + eDot = cross(ldir, eDiff); atR = sDot / (sDot - eDot); @@ -2669,7 +2669,7 @@ Shape::TesteAdjacency (Shape * a, int no, const Geom::Point atx, int nPt, diff = atx - ast; - double e = IHalfRound ((cross (diff,adir)) * a->eData[no].isqlength); + double e = IHalfRound(cross(adir, diff) * a->eData[no].isqlength); if (-3 < e && e < 3) { double rad = HalfRound (0.501); // when using single precision, 0.505 is better (0.5 would be the correct value, @@ -2684,16 +2684,16 @@ Shape::TesteAdjacency (Shape * a, int no, const Geom::Point atx, int nPt, diff4[1] = diff[1] + rad; double di1, di2; bool adjacent = false; - di1 = cross (diff1,adir); - di2 = cross (diff3,adir); + di1 = cross(adir, diff1); + di2 = cross(adir, diff3); if ((di1 < 0 && di2 > 0) || (di1 > 0 && di2 < 0)) { adjacent = true; } else { - di1 = cross ( diff2,adir); - di2 = cross (diff4,adir); + di1 = cross(adir, diff2); + di2 = cross(adir, diff4); if ((di1 < 0 && di2 > 0) || (di1 > 0 && di2 < 0)) { adjacent = true; diff --git a/src/livarot/sweep-tree.cpp b/src/livarot/sweep-tree.cpp index 7a016a2ee..1b9868f2e 100644 --- a/src/livarot/sweep-tree.cpp +++ b/src/livarot/sweep-tree.cpp @@ -117,9 +117,9 @@ SweepTree::Find(Geom::Point const &px, SweepTree *newOne, SweepTree *&insertL, nNorm=nNorm.ccw(); if (sweepSens) { - y = cross(nNorm, bNorm); - } else { y = cross(bNorm, nNorm); + } else { + y = cross(nNorm, bNorm); } if (y == 0) { y = dot(bNorm, nNorm); @@ -345,7 +345,7 @@ SweepTree::InsertAt(SweepTreeList &list, SweepEventQueue &queue, SweepTree *insertL = NULL; SweepTree *insertR = NULL; - double ang = cross(nNorm, bNorm); + double ang = cross(bNorm, nNorm); if (ang == 0) { insertL = insNode; @@ -384,7 +384,7 @@ SweepTree::InsertAt(SweepTreeList &list, SweepEventQueue &queue, { bNorm = -bNorm; } - ang = cross(nNorm, bNorm); + ang = cross(bNorm, nNorm); if (ang <= 0) { break; @@ -426,7 +426,7 @@ SweepTree::InsertAt(SweepTreeList &list, SweepEventQueue &queue, { bNorm = -bNorm; } - ang = cross(nNorm, bNorm); + ang = cross(bNorm, nNorm); if (ang > 0) { break; diff --git a/src/live_effects/CMakeLists.txt b/src/live_effects/CMakeLists.txt index 3a39b1a01..9a2f06a76 100644 --- a/src/live_effects/CMakeLists.txt +++ b/src/live_effects/CMakeLists.txt @@ -3,7 +3,6 @@ set(live_effects_SRC lpe-angle_bisector.cpp lpe-attach-path.cpp lpe-bendpath.cpp - lpe-boolops.cpp lpe-bounding-box.cpp lpe-bspline.cpp lpe-circle_3pts.cpp @@ -82,7 +81,6 @@ set(live_effects_SRC lpe-angle_bisector.h lpe-attach-path.h lpe-bendpath.h - lpe-boolops.h lpe-bounding-box.h lpe-bspline.h lpe-circle_3pts.h diff --git a/src/live_effects/Makefile_insert b/src/live_effects/Makefile_insert index 5ab61f0a6..b5bee55c8 100644 --- a/src/live_effects/Makefile_insert +++ b/src/live_effects/Makefile_insert @@ -14,8 +14,6 @@ ink_common_sources += \ live_effects/lpe-patternalongpath.h \ live_effects/lpe-bendpath.cpp \ live_effects/lpe-bendpath.h \ - live_effects/lpe-boolops.cpp \ - live_effects/lpe-boolops.h \ live_effects/lpe-dynastroke.cpp \ live_effects/lpe-dynastroke.h \ live_effects/lpe-extrude.cpp \ diff --git a/src/live_effects/effect.cpp b/src/live_effects/effect.cpp index 572d4c35c..fe7bf3a97 100644 --- a/src/live_effects/effect.cpp +++ b/src/live_effects/effect.cpp @@ -14,55 +14,54 @@ // include effects: #include "live_effects/lpe-patternalongpath.h" #include "live_effects/effect.h" +#include "live_effects/lpe-angle_bisector.h" +#include "live_effects/lpe-attach-path.h" #include "live_effects/lpe-bendpath.h" -#include "live_effects/lpe-sketch.h" -#include "live_effects/lpe-vonkoch.h" -#include "live_effects/lpe-knot.h" -#include "live_effects/lpe-rough-hatches.h" +#include "live_effects/lpe-bounding-box.h" +#include "live_effects/lpe-bspline.h" +#include "live_effects/lpe-circle_3pts.h" +#include "live_effects/lpe-circle_with_radius.h" +#include "live_effects/lpe-clone-original.h" +#include "live_effects/lpe-constructgrid.h" +#include "live_effects/lpe-copy_rotate.h" +#include "live_effects/lpe-curvestitch.h" #include "live_effects/lpe-dynastroke.h" -#include "live_effects/lpe-test-doEffect-stack.h" +#include "live_effects/lpe-ellipse_5pts.h" +#include "live_effects/lpe-envelope.h" +#include "live_effects/lpe-extrude.h" +#include "live_effects/lpe-fill-between-many.h" +#include "live_effects/lpe-fill-between-strokes.h" +#include "live_effects/lpe-fillet-chamfer.h" #include "live_effects/lpe-gears.h" -#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-interpolate.h" +#include "live_effects/lpe-interpolate_points.h" +#include "live_effects/lpe-jointype.h" +#include "live_effects/lpe-knot.h" #include "live_effects/lpe-lattice2.h" +#include "live_effects/lpe-lattice.h" +#include "live_effects/lpe-line_segment.h" +#include "live_effects/lpe-mirror_symmetry.h" +#include "live_effects/lpe-offset.h" +#include "live_effects/lpe-parallel.h" +#include "live_effects/lpe-path_length.h" +#include "live_effects/lpe-perp_bisector.h" +#include "live_effects/lpe-perspective-envelope.h" +#include "live_effects/lpe-perspective_path.h" +#include "live_effects/lpe-powerstroke.h" +#include "live_effects/lpe-recursiveskeleton.h" #include "live_effects/lpe-roughen.h" +#include "live_effects/lpe-rough-hatches.h" +#include "live_effects/lpe-ruler.h" #include "live_effects/lpe-show_handles.h" #include "live_effects/lpe-simplify.h" -#include "live_effects/lpe-envelope.h" -#include "live_effects/lpe-constructgrid.h" -#include "live_effects/lpe-perp_bisector.h" +#include "live_effects/lpe-sketch.h" +#include "live_effects/lpe-spiro.h" #include "live_effects/lpe-tangent_to_curve.h" -#include "live_effects/lpe-mirror_symmetry.h" -#include "live_effects/lpe-circle_3pts.h" #include "live_effects/lpe-transform_2pts.h" -#include "live_effects/lpe-angle_bisector.h" -#include "live_effects/lpe-parallel.h" -#include "live_effects/lpe-copy_rotate.h" -#include "live_effects/lpe-offset.h" -#include "live_effects/lpe-ruler.h" -#include "live_effects/lpe-boolops.h" -#include "live_effects/lpe-interpolate.h" -#include "live_effects/lpe-interpolate_points.h" -#include "live_effects/lpe-text_label.h" -#include "live_effects/lpe-path_length.h" -#include "live_effects/lpe-line_segment.h" -#include "live_effects/lpe-recursiveskeleton.h" -#include "live_effects/lpe-extrude.h" -#include "live_effects/lpe-powerstroke.h" -#include "live_effects/lpe-clone-original.h" -#include "live_effects/lpe-bspline.h" -#include "live_effects/lpe-attach-path.h" -#include "live_effects/lpe-fill-between-strokes.h" -#include "live_effects/lpe-fill-between-many.h" -#include "live_effects/lpe-ellipse_5pts.h" -#include "live_effects/lpe-bounding-box.h" -#include "live_effects/lpe-jointype.h" #include "live_effects/lpe-taperstroke.h" -#include "live_effects/lpe-fillet-chamfer.h" +#include "live_effects/lpe-test-doEffect-stack.h" +#include "live_effects/lpe-text_label.h" +#include "live_effects/lpe-vonkoch.h" #include "xml/node-event-vector.h" #include "sp-object.h" @@ -99,8 +98,6 @@ const Util::EnumData<EffectType> LPETypeData[] = { #ifdef LPE_ENABLE_TEST_EFFECTS {DOEFFECTSTACK_TEST, N_("doEffect stack test"), "doeffectstacktest"}, {ANGLE_BISECTOR, N_("Angle bisector"), "angle_bisector"}, - // TRANSLATORS: boolean operations - {BOOLOPS, N_("Boolops"), "boolops"}, {CIRCLE_WITH_RADIUS, N_("Circle (by center and radius)"), "circle_with_radius"}, {CIRCLE_3PTS, N_("Circle by 3 points"), "circle_3pts"}, {DYNASTROKE, N_("Dynamic stroke"), "dynastroke"}, @@ -245,9 +242,6 @@ Effect::New(EffectType lpenr, LivePathEffectObject *lpeobj) case RULER: neweffect = static_cast<Effect*> ( new LPERuler(lpeobj) ); break; - case BOOLOPS: - neweffect = static_cast<Effect*> ( new LPEBoolops(lpeobj) ); - break; case INTERPOLATE: neweffect = static_cast<Effect*> ( new LPEInterpolate(lpeobj) ); break; @@ -369,7 +363,6 @@ Effect::Effect(LivePathEffectObject *lpeobject) lpeobj(lpeobject), concatenate_before_pwd2(false), sp_lpe_item(NULL), - defaultUnit("px"), current_zoom(1), sp_curve(NULL), provides_own_flash_paths(true), // is automatically set to false if providesOwnFlashPaths() is not overridden @@ -459,7 +452,6 @@ void Effect::doOnRemove (SPLPEItem const* /*lpeitem*/) void Effect::doOnApply_impl(SPLPEItem const* lpeitem) { sp_lpe_item = const_cast<SPLPEItem *>(lpeitem); - defaultUnit = sp_lpe_item->document->getDisplayUnit()->abbr; /*sp_curve = SP_SHAPE(sp_lpe_item)->getCurve(); pathvector_before_effect = sp_curve->get_pathvector();*/ doOnApply(lpeitem); @@ -468,7 +460,6 @@ void Effect::doOnApply_impl(SPLPEItem const* lpeitem) void Effect::doBeforeEffect_impl(SPLPEItem const* lpeitem) { sp_lpe_item = const_cast<SPLPEItem *>(lpeitem); - defaultUnit = sp_lpe_item->document->getDisplayUnit()->abbr; //printf("(SPLPEITEM*) %p\n", sp_lpe_item); SPShape * shape = dynamic_cast<SPShape *>(sp_lpe_item); if(shape){ @@ -528,24 +519,24 @@ Effect::acceptParamPath (SPPath const*/*param_path*/) { void Effect::doEffect (SPCurve * curve) { - std::vector<Geom::Path> orig_pathv = curve->get_pathvector(); + Geom::PathVector orig_pathv = curve->get_pathvector(); - std::vector<Geom::Path> result_pathv = doEffect_path(orig_pathv); + Geom::PathVector result_pathv = doEffect_path(orig_pathv); curve->set_pathvector(result_pathv); } -std::vector<Geom::Path> -Effect::doEffect_path (std::vector<Geom::Path> const & path_in) +Geom::PathVector +Effect::doEffect_path (Geom::PathVector const & path_in) { - std::vector<Geom::Path> path_out; + Geom::PathVector path_out; if ( !concatenate_before_pwd2 ) { // default behavior for (unsigned int i=0; i < path_in.size(); i++) { Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_in = path_in[i].toPwSb(); Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_out = doEffect_pwd2(pwd2_in); - std::vector<Geom::Path> path = Geom::path_from_piecewise( pwd2_out, LPE_CONVERSION_TOLERANCE); + Geom::PathVector path = Geom::path_from_piecewise( pwd2_out, LPE_CONVERSION_TOLERANCE); // add the output path vector to the already accumulated vector: for (unsigned int j=0; j < path.size(); j++) { path_out.push_back(path[j]); diff --git a/src/live_effects/effect.h b/src/live_effects/effect.h index ac1f0b8dc..ea57ff243 100644 --- a/src/live_effects/effect.h +++ b/src/live_effects/effect.h @@ -129,9 +129,9 @@ protected: // of what kind of input/output parameters he desires. // the order in which they appear is the order in which they are // called by this base class. (i.e. doEffect(SPCurve * curve) defaults to calling - // doEffect(std::vector<Geom::Path> ) - virtual std::vector<Geom::Path> - doEffect_path (std::vector<Geom::Path> const & path_in); + // doEffect(Geom::PathVector ) + virtual Geom::PathVector + doEffect_path (Geom::PathVector const & path_in); virtual Geom::Piecewise<Geom::D2<Geom::SBasis> > doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in); @@ -159,11 +159,10 @@ protected: bool concatenate_before_pwd2; SPLPEItem * sp_lpe_item; // these get stored in doBeforeEffect_impl, and derived classes may do as they please with them. - Glib::ustring defaultUnit; // these get stored in doBeforeEffect_impl, and derived classes may do as they please with them. double current_zoom; std::vector<Geom::Point> selectedNodesPoints; SPCurve * sp_curve; - std::vector<Geom::Path> pathvector_before_effect; + Geom::PathVector pathvector_before_effect; private: bool provides_own_flash_paths; // if true, the standard flash path is suppressed diff --git a/src/live_effects/lpe-angle_bisector.cpp b/src/live_effects/lpe-angle_bisector.cpp index 1acf9605f..95a81c763 100644 --- a/src/live_effects/lpe-angle_bisector.cpp +++ b/src/live_effects/lpe-angle_bisector.cpp @@ -56,8 +56,8 @@ LPEAngleBisector::~LPEAngleBisector() { } -std::vector<Geom::Path> -LPEAngleBisector::doEffect_path (std::vector<Geom::Path> const & path_in) +Geom::PathVector +LPEAngleBisector::doEffect_path (Geom::PathVector const & path_in) { using namespace Geom; @@ -73,7 +73,7 @@ LPEAngleBisector::doEffect_path (std::vector<Geom::Path> const & path_in) Geom::Point D = ptA - dir * length_left; Geom::Point E = ptA + dir * length_right; - Piecewise<D2<SBasis> > output = Piecewise<D2<SBasis> >(D2<SBasis>(Linear(D[X], E[X]), Linear(D[Y], E[Y]))); + Piecewise<D2<SBasis> > output = Piecewise<D2<SBasis> >(D2<SBasis>(SBasis(D[X], E[X]), SBasis(D[Y], E[Y]))); return path_from_piecewise(output, LPE_CONVERSION_TOLERANCE); } @@ -103,7 +103,7 @@ KnotHolderEntityLeftEnd::knot_set(Geom::Point const &p, Geom::Point const &/*ori Geom::Point const s = snap_knot_position(p, state); - double lambda = Geom::nearest_point(s, lpe->ptA, lpe->dir); + double lambda = Geom::nearest_time(s, lpe->ptA, lpe->dir); lpe->length_left.param_set_value(-lambda); sp_lpe_item_update_patheffect (SP_LPE_ITEM(item), false, true); @@ -116,7 +116,7 @@ KnotHolderEntityRightEnd::knot_set(Geom::Point const &p, Geom::Point const &/*or Geom::Point const s = snap_knot_position(p, state); - double lambda = Geom::nearest_point(s, lpe->ptA, lpe->dir); + double lambda = Geom::nearest_time(s, lpe->ptA, lpe->dir); lpe->length_right.param_set_value(lambda); sp_lpe_item_update_patheffect (SP_LPE_ITEM(item), false, true); diff --git a/src/live_effects/lpe-angle_bisector.h b/src/live_effects/lpe-angle_bisector.h index 95048751e..400cbf6b6 100644 --- a/src/live_effects/lpe-angle_bisector.h +++ b/src/live_effects/lpe-angle_bisector.h @@ -28,7 +28,7 @@ public: LPEAngleBisector(LivePathEffectObject *lpeobject); virtual ~LPEAngleBisector(); - virtual std::vector<Geom::Path> doEffect_path (std::vector<Geom::Path> const & path_in); + virtual Geom::PathVector doEffect_path (Geom::PathVector const & path_in); friend class AB::KnotHolderEntityLeftEnd; friend class AB::KnotHolderEntityRightEnd; diff --git a/src/live_effects/lpe-attach-path.cpp b/src/live_effects/lpe-attach-path.cpp index 768c66ee2..21459f322 100644 --- a/src/live_effects/lpe-attach-path.cpp +++ b/src/live_effects/lpe-attach-path.cpp @@ -64,7 +64,7 @@ void LPEAttachPath::resetDefaults(SPItem const * /*item*/) void LPEAttachPath::doEffect (SPCurve * curve) { - std::vector<Geom::Path> this_pathv = curve->get_pathvector(); + Geom::PathVector this_pathv = curve->get_pathvector(); if (sp_lpe_item && !this_pathv.empty()) { Geom::Path p = Geom::Path(this_pathv.front().initialPoint()); @@ -73,7 +73,7 @@ void LPEAttachPath::doEffect (SPCurve * curve) if (start_path.linksToPath()) { - std::vector<Geom::Path> linked_pathv = start_path.get_pathvector(); + Geom::PathVector linked_pathv = start_path.get_pathvector(); Geom::Affine linkedtransform = start_path.getObject()->getRelativeTransform(sp_lpe_item); if ( !linked_pathv.empty() ) @@ -87,7 +87,7 @@ void LPEAttachPath::doEffect (SPCurve * curve) Geom::Coord length = derivs[deriv_n].length(); if ( ! Geom::are_near(length, 0) ) { if (set_start_end) { - start_path_position.param_set_value(transformedpath.nearestPoint(start_path_curve_end.getOrigin())); + start_path_position.param_set_value(transformedpath.nearestTime(start_path_curve_end.getOrigin()).asFlatTime()); } if (start_path_position > transformedpath.size()) { @@ -95,7 +95,8 @@ void LPEAttachPath::doEffect (SPCurve * curve) } else if (start_path_position < 0) { start_path_position.param_set_value(0); } - const Geom::Curve *c = start_path_position >= transformedpath.size() ? &transformedpath.back() : &transformedpath.at_index((int)start_path_position); + Geom::Curve const *c = start_path_position >= transformedpath.size() ? + &transformedpath.back() : &transformedpath.at((int)start_path_position); std::vector<Geom::Point> derivs_2 = c->pointAndDerivatives(start_path_position >= transformedpath.size() ? 1 : (start_path_position - (int)start_path_position), 3); for (unsigned deriv_n_2 = 1; deriv_n_2 < derivs_2.size(); deriv_n_2++) { @@ -126,7 +127,7 @@ void LPEAttachPath::doEffect (SPCurve * curve) if (end_path.linksToPath()) { - std::vector<Geom::Path> linked_pathv = end_path.get_pathvector(); + Geom::PathVector linked_pathv = end_path.get_pathvector(); Geom::Affine linkedtransform = end_path.getObject()->getRelativeTransform(sp_lpe_item); if ( !linked_pathv.empty() ) @@ -141,7 +142,7 @@ void LPEAttachPath::doEffect (SPCurve * curve) Geom::Coord length = derivs[deriv_n].length(); if ( ! Geom::are_near(length, 0) ) { if (set_end_end) { - end_path_position.param_set_value(transformedpath.nearestPoint(end_path_curve_end.getOrigin())); + end_path_position.param_set_value(transformedpath.nearestTime(end_path_curve_end.getOrigin()).asFlatTime()); } if (end_path_position > transformedpath.size()) { @@ -149,7 +150,8 @@ void LPEAttachPath::doEffect (SPCurve * curve) } else if (end_path_position < 0) { end_path_position.param_set_value(0); } - const Geom::Curve *c = end_path_position >= transformedpath.size() ? &transformedpath.back() : &transformedpath.at_index((int)end_path_position); + const Geom::Curve *c = end_path_position >= transformedpath.size() ? + &transformedpath.back() : &transformedpath.at((int)end_path_position); std::vector<Geom::Point> derivs_2 = c->pointAndDerivatives(end_path_position >= transformedpath.size() ? 1 : (end_path_position - (int)end_path_position), 3); for (unsigned deriv_n_2 = 1; deriv_n_2 < derivs_2.size(); deriv_n_2++) { diff --git a/src/live_effects/lpe-boolops.cpp b/src/live_effects/lpe-boolops.cpp deleted file mode 100644 index 641cf5d50..000000000 --- a/src/live_effects/lpe-boolops.cpp +++ /dev/null @@ -1,79 +0,0 @@ -/** \file - * LPE boolops implementation - */ -/* - * Authors: - * Johan Engelen - * - * Copyright (C) Johan Engelen 2007-2008 <j.b.c.engelen@utwente.nl> - * - * Released under GNU GPL, read the file 'COPYING' for more information - */ - -#include "live_effects/lpe-boolops.h" - -#include <2geom/path.h> -#include <2geom/shape.h> - -namespace Inkscape { -namespace LivePathEffect { - -static const Util::EnumData<unsigned> BoolopTypeData[] = { - {Geom::BOOLOP_NULL , N_("Null"), "null"}, - {Geom::BOOLOP_INTERSECT , N_("Intersect"), "intersect"}, - {Geom::BOOLOP_SUBTRACT_A_B , N_("Subtract A-B"), "subtract_a_b"}, - {Geom::BOOLOP_IDENTITY_A , N_("Identity A"), "identity_a"}, - {Geom::BOOLOP_SUBTRACT_B_A , N_("Subtract B-A"), "subtract_b_a"}, - {Geom::BOOLOP_IDENTITY_B , N_("Identity B"), "identity_b"}, - {Geom::BOOLOP_EXCLUSION , N_("Exclusion"), "Exclusion"}, - {Geom::BOOLOP_UNION , N_("Union"), "Union"} -}; -static const Util::EnumDataConverter<unsigned> BoolopTypeConverter(BoolopTypeData, sizeof(BoolopTypeData)/sizeof(*BoolopTypeData)); - -LPEBoolops::LPEBoolops(LivePathEffectObject *lpeobject) : - Effect(lpeobject), - bool_path(_("2nd path:"), _("Path to which the original path will be boolop'ed."), "path_2nd", &wr, this, "M0,0 L1,0"), - boolop_type(_("Boolop type:"), _("Determines which kind of boolop will be performed."), "boolop_type", BoolopTypeConverter, &wr, this, Geom::BOOLOP_UNION) -{ - show_orig_path = true; - - registerParameter( dynamic_cast<Parameter *>(&boolop_type) ); - registerParameter( dynamic_cast<Parameter *>(&bool_path) ); -} - -LPEBoolops::~LPEBoolops() -{ - -} - - -Geom::PathVector -LPEBoolops::doEffect_path (Geom::PathVector const & path_in) -{ - std::vector<Geom::Path> path_out; - - Geom::Shape shape_in = Geom::sanitize(path_in); - - Geom::Shape shape_param = Geom::sanitize(bool_path.get_pathvector()); - - Geom::Shape shape_out = Geom::boolop(shape_in, shape_param, boolop_type.get_value()); - - path_out = Geom::desanitize(shape_out); - - return path_out; -} - - -} //namespace LivePathEffect -} /* 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 : diff --git a/src/live_effects/lpe-boolops.h b/src/live_effects/lpe-boolops.h deleted file mode 100644 index 3c8dc85c4..000000000 --- a/src/live_effects/lpe-boolops.h +++ /dev/null @@ -1,53 +0,0 @@ -#ifndef INKSCAPE_LPE_BOOLOPS_H -#define INKSCAPE_LPE_BOOLOPS_H - -/** \file - * LPE boolops implementation, see lpe-boolops.cpp. - */ - -/* - * Authors: - * Johan Engelen - * - * Copyright (C) Johan Engelen 2007-2008 <j.b.c.engelen@utwente.nl> - * - * Released under GNU GPL, read the file 'COPYING' for more information - */ - -#include "live_effects/parameter/enum.h" -#include "live_effects/effect.h" -#include "live_effects/parameter/path.h" - -namespace Inkscape { -namespace LivePathEffect { - -class LPEBoolops : public Effect { -public: - LPEBoolops(LivePathEffectObject *lpeobject); - virtual ~LPEBoolops(); - - virtual std::vector<Geom::Path> doEffect_path (std::vector<Geom::Path> const & path_in); - -private: - PathParam bool_path; - EnumParam<unsigned> boolop_type; - - LPEBoolops(const LPEBoolops&); - LPEBoolops& operator=(const LPEBoolops&); -}; - -} //namespace LivePathEffect -} //namespace Inkscape - -#endif // INKSCAPE_LPE_BOOLOPS_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 : diff --git a/src/live_effects/lpe-bounding-box.cpp b/src/live_effects/lpe-bounding-box.cpp index bafd5e70e..43a60d482 100644 --- a/src/live_effects/lpe-bounding-box.cpp +++ b/src/live_effects/lpe-bounding-box.cpp @@ -45,7 +45,7 @@ void LPEBoundingBox::doEffect (SPCurve * curve) p.appendNew<Geom::LineSegment>(Geom::Point(bbox->right(), bbox->bottom())); p.appendNew<Geom::LineSegment>(Geom::Point(bbox->left(), bbox->bottom())); p.appendNew<Geom::LineSegment>(Geom::Point(bbox->left(), bbox->top())); - std::vector<Geom::Path> out; + Geom::PathVector out; out.push_back(p); curve->set_pathvector(out); } diff --git a/src/live_effects/lpe-bspline.cpp b/src/live_effects/lpe-bspline.cpp index ecbfef76a..c2a2d080e 100644 --- a/src/live_effects/lpe-bspline.cpp +++ b/src/live_effects/lpe-bspline.cpp @@ -9,6 +9,7 @@ #include "sp-path.h" #include "svg/svg.h" #include "xml/repr.h" +#include "preferences.h" // TODO due to internal breakage in glibmm headers, this must be last: #include <glibmm/i18n.h> @@ -43,7 +44,7 @@ LPEBSpline::LPEBSpline(LivePathEffectObject *lpeobject) steps.param_set_digits(0); helper_size.param_set_range(0.0, 999.0); - helper_size.param_set_increments(5, 5); + helper_size.param_set_increments(1, 1); helper_size.param_set_digits(2); } @@ -76,13 +77,15 @@ void LPEBSpline::doEffect(SPCurve *curve) Geom::PathVector const original_pathv = curve->get_pathvector(); curve->reset(); - + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); for (Geom::PathVector::const_iterator path_it = original_pathv.begin(); path_it != original_pathv.end(); ++path_it) { if (path_it->empty()) { continue; } - hp.push_back(*path_it); + if (!prefs->getBool("/tools/nodes/show_outline", true)){ + hp.push_back(*path_it); + } 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(); @@ -121,12 +124,12 @@ void LPEBSpline::doEffect(SPCurve *curve) if(are_near((*cubic)[1],(*cubic)[0]) && !are_near((*cubic)[2],(*cubic)[3])) { point_at1 = sbasis_in.valueAt(DEFAULT_START_POWER); } else { - point_at1 = sbasis_in.valueAt(Geom::nearest_point((*cubic)[1], *in->first_segment())); + point_at1 = sbasis_in.valueAt(Geom::nearest_time((*cubic)[1], *in->first_segment())); } if(are_near((*cubic)[2],(*cubic)[3]) && !are_near((*cubic)[1],(*cubic)[0])) { point_at2 = sbasis_in.valueAt(DEFAULT_END_POWER); } else { - point_at2 = sbasis_in.valueAt(Geom::nearest_point((*cubic)[2], *in->first_segment())); + point_at2 = sbasis_in.valueAt(Geom::nearest_time((*cubic)[2], *in->first_segment())); } } else { point_at1 = in->first_segment()->initialPoint(); @@ -144,7 +147,7 @@ void LPEBSpline::doEffect(SPCurve *curve) if(are_near((*cubic)[1],(*cubic)[0]) && !are_near((*cubic)[2],(*cubic)[3])) { next_point_at1 = sbasis_in.valueAt(DEFAULT_START_POWER); } else { - next_point_at1 = sbasis_out.valueAt(Geom::nearest_point((*cubic)[1], *out->first_segment())); + next_point_at1 = sbasis_out.valueAt(Geom::nearest_time((*cubic)[1], *out->first_segment())); } } else { next_point_at1 = out->first_segment()->initialPoint(); @@ -161,7 +164,7 @@ void LPEBSpline::doEffect(SPCurve *curve) cubic = dynamic_cast<Geom::CubicBezier const *>(&*path_it->begin()); if (cubic) { line_helper->moveto(sbasis_start.valueAt( - Geom::nearest_point((*cubic)[1], *start->first_segment()))); + Geom::nearest_time((*cubic)[1], *start->first_segment()))); } else { line_helper->moveto(start->first_segment()->initialPoint()); } @@ -175,7 +178,7 @@ void LPEBSpline::doEffect(SPCurve *curve) cubic = dynamic_cast<Geom::CubicBezier const *>(&*curve_it1); if (cubic) { line_helper->lineto(sbasis_end.valueAt( - Geom::nearest_point((*cubic)[2], *end->first_segment()))); + Geom::nearest_time((*cubic)[2], *end->first_segment()))); } else { line_helper->lineto(end->first_segment()->finalPoint()); } @@ -233,7 +236,7 @@ LPEBSpline::drawHandle(Geom::Point p, double helper_size) Geom::Affine aff = Geom::Affine(); aff *= Geom::Scale(helper_size); pathv *= aff; - pathv += p - Geom::Point(0.5*helper_size, 0.5*helper_size); + pathv *= Geom::Translate(p - Geom::Point(0.5*helper_size, 0.5*helper_size)); hp.push_back(pathv[0]); } diff --git a/src/live_effects/lpe-circle_3pts.cpp b/src/live_effects/lpe-circle_3pts.cpp index fb7496f58..dbb1f4b6b 100644 --- a/src/live_effects/lpe-circle_3pts.cpp +++ b/src/live_effects/lpe-circle_3pts.cpp @@ -17,6 +17,7 @@ // You might need to include other 2geom files. You can add them here: #include <2geom/path.h> #include <2geom/circle.h> +#include <2geom/path-sink.h> namespace Inkscape { namespace LivePathEffect { @@ -30,7 +31,7 @@ LPECircle3Pts::~LPECircle3Pts() { } -static void _circle3(Geom::Point const &A, Geom::Point const &B, Geom::Point const &C, std::vector<Geom::Path> &path_out) { +static void _circle3(Geom::Point const &A, Geom::Point const &B, Geom::Point const &C, Geom::PathVector &path_out) { using namespace Geom; Point D = (A + B)/2; @@ -47,13 +48,13 @@ static void _circle3(Geom::Point const &A, Geom::Point const &B, Geom::Point con double radius = L2(M - A); Geom::Circle c(M, radius); - c.getPath(path_out); + path_out = Geom::Path(c); } -std::vector<Geom::Path> -LPECircle3Pts::doEffect_path (std::vector<Geom::Path> const & path_in) +Geom::PathVector +LPECircle3Pts::doEffect_path (Geom::PathVector const & path_in) { - std::vector<Geom::Path> path_out = std::vector<Geom::Path>(); + Geom::PathVector path_out = Geom::PathVector(); // we assume that the path has >= 3 nodes Geom::Point A = path_in[0].initialPoint(); diff --git a/src/live_effects/lpe-circle_3pts.h b/src/live_effects/lpe-circle_3pts.h index 2533fe251..07c2eddb0 100644 --- a/src/live_effects/lpe-circle_3pts.h +++ b/src/live_effects/lpe-circle_3pts.h @@ -27,7 +27,7 @@ public: LPECircle3Pts(LivePathEffectObject *lpeobject); virtual ~LPECircle3Pts(); - virtual std::vector<Geom::Path> doEffect_path (std::vector<Geom::Path> const & path_in); + virtual Geom::PathVector doEffect_path (Geom::PathVector const & path_in); private: LPECircle3Pts(const LPECircle3Pts&); diff --git a/src/live_effects/lpe-circle_with_radius.cpp b/src/live_effects/lpe-circle_with_radius.cpp index 8a32cd230..8f2156044 100644 --- a/src/live_effects/lpe-circle_with_radius.cpp +++ b/src/live_effects/lpe-circle_with_radius.cpp @@ -15,11 +15,9 @@ #include "display/curve.h" // You might need to include other 2geom files. You can add them here: -#include <2geom/path.h> -#include <2geom/sbasis.h> -#include <2geom/bezier-to-sbasis.h> -#include <2geom/d2.h> +#include <2geom/pathvector.h> #include <2geom/circle.h> +#include <2geom/path-sink.h> using namespace Geom; @@ -40,10 +38,10 @@ LPECircleWithRadius::~LPECircleWithRadius() } -std::vector<Geom::Path> -LPECircleWithRadius::doEffect_path (std::vector<Geom::Path> const & path_in) +Geom::PathVector +LPECircleWithRadius::doEffect_path (Geom::PathVector const & path_in) { - std::vector<Geom::Path> path_out = std::vector<Geom::Path>(); + Geom::PathVector path_out = Geom::PathVector(); Geom::Point center = path_in[0].initialPoint(); Geom::Point pt = path_in[0].finalPoint(); @@ -51,9 +49,7 @@ LPECircleWithRadius::doEffect_path (std::vector<Geom::Path> const & path_in) double radius = Geom::L2(pt - center); Geom::Circle c(center, radius); - c.getPath(path_out); - - return path_out; + return Geom::Path(c); } /* diff --git a/src/live_effects/lpe-circle_with_radius.h b/src/live_effects/lpe-circle_with_radius.h index 10f652771..f5bf0c414 100644 --- a/src/live_effects/lpe-circle_with_radius.h +++ b/src/live_effects/lpe-circle_with_radius.h @@ -26,7 +26,7 @@ public: virtual ~LPECircleWithRadius(); // Choose to implement one of the doEffect functions. You can delete or comment out the others. - virtual std::vector<Geom::Path> doEffect_path (std::vector<Geom::Path> const & path_in); + virtual Geom::PathVector doEffect_path (Geom::PathVector const & path_in); private: // add the parameters for your effect here: diff --git a/src/live_effects/lpe-clone-original.cpp b/src/live_effects/lpe-clone-original.cpp index d7cd6e19c..7541c0be2 100644 --- a/src/live_effects/lpe-clone-original.cpp +++ b/src/live_effects/lpe-clone-original.cpp @@ -28,7 +28,7 @@ LPECloneOriginal::~LPECloneOriginal() void LPECloneOriginal::doEffect (SPCurve * curve) { if ( linked_path.linksToPath() ) { - std::vector<Geom::Path> linked_pathv = linked_path.get_pathvector(); + Geom::PathVector linked_pathv = linked_path.get_pathvector(); if ( !linked_pathv.empty() ) { curve->set_pathvector(linked_pathv); } diff --git a/src/live_effects/lpe-constructgrid.cpp b/src/live_effects/lpe-constructgrid.cpp index fbf9faf56..b1e0edaac 100644 --- a/src/live_effects/lpe-constructgrid.cpp +++ b/src/live_effects/lpe-constructgrid.cpp @@ -41,8 +41,8 @@ LPEConstructGrid::~LPEConstructGrid() } -std::vector<Geom::Path> -LPEConstructGrid::doEffect_path (std::vector<Geom::Path> const & path_in) +Geom::PathVector +LPEConstructGrid::doEffect_path (Geom::PathVector const & path_in) { // Check that the path has at least 3 nodes (i.e. 2 segments), more nodes are ignored if (path_in[0].size() >= 2) { @@ -64,7 +64,7 @@ LPEConstructGrid::doEffect_path (std::vector<Geom::Path> const & path_in) second_path.appendNew<LineSegment>( origin + second_p*nr_x ); // use the gridpaths and set them in the correct grid - std::vector<Geom::Path> path_out; + Geom::PathVector path_out; path_out.push_back(first_path); for (int ix = 0; ix < nr_x; ix++) { path_out.push_back(path_out.back() * second_translation ); diff --git a/src/live_effects/lpe-constructgrid.h b/src/live_effects/lpe-constructgrid.h index c7e695794..49c986742 100644 --- a/src/live_effects/lpe-constructgrid.h +++ b/src/live_effects/lpe-constructgrid.h @@ -25,7 +25,7 @@ public: LPEConstructGrid(LivePathEffectObject *lpeobject); virtual ~LPEConstructGrid(); - virtual std::vector<Geom::Path> doEffect_path (std::vector<Geom::Path> const & path_in); + virtual Geom::PathVector doEffect_path (Geom::PathVector const & path_in); private: ScalarParam nr_x; diff --git a/src/live_effects/lpe-curvestitch.cpp b/src/live_effects/lpe-curvestitch.cpp index 854cc0734..609447f26 100644 --- a/src/live_effects/lpe-curvestitch.cpp +++ b/src/live_effects/lpe-curvestitch.cpp @@ -68,8 +68,8 @@ LPECurveStitch::~LPECurveStitch() } -std::vector<Geom::Path> -LPECurveStitch::doEffect_path (std::vector<Geom::Path> const & path_in) +Geom::PathVector +LPECurveStitch::doEffect_path (Geom::PathVector const & path_in) { if (path_in.size() >= 2) { startpoint_edge_variation.resetRandomizer(); @@ -86,7 +86,7 @@ LPECurveStitch::doEffect_path (std::vector<Geom::Path> const & path_in) gdouble scaling = bndsStroke->max() - bndsStroke->min(); Point stroke_origin(bndsStroke->min(), (bndsStrokeY->max()+bndsStrokeY->min())/2); - std::vector<Geom::Path> path_out; + Geom::PathVector path_out; // do this for all permutations (ii,jj) if there are more than 2 paths? realllly cool! for (unsigned ii = 0 ; ii < path_in.size() - 1; ii++) @@ -127,7 +127,7 @@ LPECurveStitch::doEffect_path (std::vector<Geom::Path> const & path_in) // add stuff to one big pw<d2<sbasis> > and then outside the loop convert to path? // No: this way, the separate result paths are kept separate which might come in handy some time! - std::vector<Geom::Path> result = Geom::path_from_piecewise(pwd2_out, LPE_CONVERSION_TOLERANCE); + Geom::PathVector result = Geom::path_from_piecewise(pwd2_out, LPE_CONVERSION_TOLERANCE); path_out.push_back(result[0]); } gdouble svA = startpoint_spacing_variation - startpoint_spacing_variation.get_value()/2; @@ -162,7 +162,7 @@ LPECurveStitch::resetDefaults(SPItem const* item) // calculate bounding box: (isn't there a simpler way?) Piecewise<D2<SBasis> > pwd2; - std::vector<Geom::Path> temppath = sp_svg_read_pathv( item->getRepr()->attribute("inkscape:original-d")); + Geom::PathVector temppath = sp_svg_read_pathv( item->getRepr()->attribute("inkscape:original-d")); for (unsigned int i=0; i < temppath.size(); i++) { pwd2.concat( temppath[i].toPwSb() ); } diff --git a/src/live_effects/lpe-curvestitch.h b/src/live_effects/lpe-curvestitch.h index 38a11b4af..c6ea66f6c 100644 --- a/src/live_effects/lpe-curvestitch.h +++ b/src/live_effects/lpe-curvestitch.h @@ -28,7 +28,7 @@ public: LPECurveStitch(LivePathEffectObject *lpeobject); virtual ~LPECurveStitch(); - virtual std::vector<Geom::Path> doEffect_path (std::vector<Geom::Path> const & path_in); + virtual Geom::PathVector doEffect_path (Geom::PathVector const & path_in); virtual void resetDefaults(SPItem const* item); diff --git a/src/live_effects/lpe-ellipse_5pts.cpp b/src/live_effects/lpe-ellipse_5pts.cpp index b0a5919fe..4c953bcda 100644 --- a/src/live_effects/lpe-ellipse_5pts.cpp +++ b/src/live_effects/lpe-ellipse_5pts.cpp @@ -67,10 +67,10 @@ static double _det5(double (*mat)[5]) return mat[4][4]; } -std::vector<Geom::Path> -LPEEllipse5Pts::doEffect_path (std::vector<Geom::Path> const & path_in) +Geom::PathVector +LPEEllipse5Pts::doEffect_path (Geom::PathVector const & path_in) { - std::vector<Geom::Path> path_out = std::vector<Geom::Path>(); + Geom::PathVector path_out = Geom::PathVector(); if (path_in[0].size() < 4) { @@ -190,7 +190,7 @@ LPEEllipse5Pts::doEffect_path (std::vector<Geom::Path> const & path_in) p.appendNew<Geom::CubicBezier>(Geom::Point(x1,y1), Geom::Point(x2,y2), Geom::Point(x3,y3)); } - Geom::Affine aff = Geom::Scale(el.ray(Geom::X), el.ray(Geom::Y)) * Geom::Rotate(el.rot_angle()) * Geom::Translate(el.center()); + Geom::Affine aff = Geom::Scale(el.rays()) * Geom::Rotate(el.rotationAngle()) * Geom::Translate(el.center()); path_out.push_back(p * aff); diff --git a/src/live_effects/lpe-ellipse_5pts.h b/src/live_effects/lpe-ellipse_5pts.h index d3b1fccfa..691a693dc 100644 --- a/src/live_effects/lpe-ellipse_5pts.h +++ b/src/live_effects/lpe-ellipse_5pts.h @@ -26,7 +26,7 @@ public: LPEEllipse5Pts(LivePathEffectObject *lpeobject); virtual ~LPEEllipse5Pts(); - virtual std::vector<Geom::Path> doEffect_path (std::vector<Geom::Path> const & path_in); + virtual Geom::PathVector doEffect_path (Geom::PathVector const & path_in); private: LPEEllipse5Pts(const LPEEllipse5Pts&); diff --git a/src/live_effects/lpe-fill-between-many.cpp b/src/live_effects/lpe-fill-between-many.cpp index 7cf354044..3e0810cfc 100644 --- a/src/live_effects/lpe-fill-between-many.cpp +++ b/src/live_effects/lpe-fill-between-many.cpp @@ -35,14 +35,14 @@ LPEFillBetweenMany::~LPEFillBetweenMany() void LPEFillBetweenMany::doEffect (SPCurve * curve) { - std::vector<Geom::Path> res_pathv; + Geom::PathVector res_pathv; SPItem * firstObj = NULL; for (std::vector<PathAndDirection*>::iterator iter = linked_paths._vector.begin(); iter != linked_paths._vector.end(); iter++) { SPObject *obj; if ((*iter)->ref.isAttached() && (obj = (*iter)->ref.getObject()) && SP_IS_ITEM(obj) && !(*iter)->_pathvector.empty()) { Geom::Path linked_path; if ((*iter)->reversed) { - linked_path = (*iter)->_pathvector.front().reverse(); + linked_path = (*iter)->_pathvector.front().reversed(); } else { linked_path = (*iter)->_pathvector.front(); } diff --git a/src/live_effects/lpe-fill-between-strokes.cpp b/src/live_effects/lpe-fill-between-strokes.cpp index e72979ed0..89ea80545 100644 --- a/src/live_effects/lpe-fill-between-strokes.cpp +++ b/src/live_effects/lpe-fill-between-strokes.cpp @@ -39,17 +39,17 @@ void LPEFillBetweenStrokes::doEffect (SPCurve * curve) { if (curve) { if ( linked_path.linksToPath() && second_path.linksToPath() && linked_path.getObject() && second_path.getObject() ) { - std::vector<Geom::Path> linked_pathv = linked_path.get_pathvector(); - std::vector<Geom::Path> second_pathv = second_path.get_pathvector(); - std::vector<Geom::Path> result_linked_pathv; - std::vector<Geom::Path> result_second_pathv; + Geom::PathVector linked_pathv = linked_path.get_pathvector(); + Geom::PathVector second_pathv = second_path.get_pathvector(); + Geom::PathVector result_linked_pathv; + Geom::PathVector result_second_pathv; Geom::Affine second_transform = second_path.getObject()->getRelativeTransform(linked_path.getObject()); - for (std::vector<Geom::Path>::iterator iter = linked_pathv.begin(); iter != linked_pathv.end(); ++iter) + for (Geom::PathVector::iterator iter = linked_pathv.begin(); iter != linked_pathv.end(); ++iter) { result_linked_pathv.push_back((*iter)); } - for (std::vector<Geom::Path>::iterator iter = second_pathv.begin(); iter != second_pathv.end(); ++iter) + for (Geom::PathVector::iterator iter = second_pathv.begin(); iter != second_pathv.end(); ++iter) { result_second_pathv.push_back((*iter) * second_transform); } @@ -58,7 +58,7 @@ void LPEFillBetweenStrokes::doEffect (SPCurve * curve) if (reverse_second.get_value()) { result_linked_pathv.front().appendNew<Geom::LineSegment>(result_second_pathv.front().finalPoint()); - result_linked_pathv.front().append(result_second_pathv.front().reverse()); + result_linked_pathv.front().append(result_second_pathv.front().reversed()); } else { @@ -75,10 +75,10 @@ void LPEFillBetweenStrokes::doEffect (SPCurve * curve) } } else if ( linked_path.linksToPath() && linked_path.getObject() ) { - std::vector<Geom::Path> linked_pathv = linked_path.get_pathvector(); - std::vector<Geom::Path> result_pathv; + Geom::PathVector linked_pathv = linked_path.get_pathvector(); + Geom::PathVector result_pathv; - for (std::vector<Geom::Path>::iterator iter = linked_pathv.begin(); iter != linked_pathv.end(); ++iter) + for (Geom::PathVector::iterator iter = linked_pathv.begin(); iter != linked_pathv.end(); ++iter) { result_pathv.push_back((*iter)); } @@ -87,10 +87,10 @@ void LPEFillBetweenStrokes::doEffect (SPCurve * curve) } } else if ( second_path.linksToPath() && second_path.getObject() ) { - std::vector<Geom::Path> second_pathv = second_path.get_pathvector(); - std::vector<Geom::Path> result_pathv; + Geom::PathVector second_pathv = second_path.get_pathvector(); + Geom::PathVector result_pathv; - for (std::vector<Geom::Path>::iterator iter = second_pathv.begin(); iter != second_pathv.end(); ++iter) + for (Geom::PathVector::iterator iter = second_pathv.begin(); iter != second_pathv.end(); ++iter) { result_pathv.push_back((*iter)); } diff --git a/src/live_effects/lpe-fillet-chamfer.cpp b/src/live_effects/lpe-fillet-chamfer.cpp index c8458b8cb..a9a96864a 100644 --- a/src/live_effects/lpe-fillet-chamfer.cpp +++ b/src/live_effects/lpe-fillet-chamfer.cpp @@ -16,7 +16,7 @@ #include "live_effects/lpe-fillet-chamfer.h" #include <2geom/sbasis-to-bezier.h> -#include <2geom/svg-elliptical-arc.h> +#include <2geom/elliptical-arc.h> #include <2geom/line.h> #include "desktop.h" @@ -29,7 +29,6 @@ // for programmatically updating knots #include "ui/tools-switch.h" -#include <util/units.h> // TODO due to internal breakage in glibmm headers, this must be last: #include <glibmm/i18n.h> @@ -57,7 +56,6 @@ LPEFilletChamfer::LPEFilletChamfer(LivePathEffectObject *lpeobject) : only_selected(_("Change only selected nodes"), _("Change only selected nodes"), "only_selected", &wr, this, false), flexible(_("Flexible radius size (%)"), _("Flexible radius size (%)"), "flexible", &wr, this, false), use_knot_distance(_("Use knots distance instead radius"), _("Use knots distance instead radius"), "use_knot_distance", &wr, this, false), - unit(_("Unit:"), _("Unit"), "unit", &wr, this), method(_("Method:"), _("Fillets methods"), "method", FMConverter, &wr, this, FM_AUTO), radius(_("Radius (unit or %):"), _("Radius, in unit or %"), "radius", &wr, this, 0.), chamfer_steps(_("Chamfer steps:"), _("Chamfer steps"), "chamfer_steps", &wr, this, 0), @@ -65,7 +63,6 @@ LPEFilletChamfer::LPEFilletChamfer(LivePathEffectObject *lpeobject) : helper_size(_("Helper size with direction:"), _("Helper size with direction"), "helper_size", &wr, this, 0) { registerParameter(&fillet_chamfer_values); - registerParameter(&unit); registerParameter(&method); registerParameter(&radius); registerParameter(&chamfer_steps); @@ -223,7 +220,7 @@ void LPEFilletChamfer::updateFillet() { double power = 0; if (!flexible) { - power = Inkscape::Util::Quantity::convert(radius, unit.get_abbreviation(), defaultUnit) * -1; + power = radius * -1; } else { power = radius; } @@ -275,11 +272,11 @@ void LPEFilletChamfer::refreshKnots() } } -void LPEFilletChamfer::doUpdateFillet(std::vector<Geom::Path> const& original_pathv, double power) +void LPEFilletChamfer::doUpdateFillet(Geom::PathVector const &original_pathv, double power) { std::vector<Point> filletChamferData = fillet_chamfer_values.data(); std::vector<Geom::Point> result; - std::vector<Geom::Path> original_pathv_processed = pathv_to_linear_and_cubic_beziers(original_pathv); + Geom::PathVector original_pathv_processed = pathv_to_linear_and_cubic_beziers(original_pathv); int counter = 0; for (PathVector::const_iterator path_it = original_pathv_processed.begin(); path_it != original_pathv_processed.end(); ++path_it) { @@ -323,11 +320,11 @@ void LPEFilletChamfer::doUpdateFillet(std::vector<Geom::Path> const& original_pa fillet_chamfer_values.param_set_and_write_new_value(result); } -void LPEFilletChamfer::doChangeType(std::vector<Geom::Path> const& original_pathv, int type) +void LPEFilletChamfer::doChangeType(Geom::PathVector const &original_pathv, int type) { std::vector<Point> filletChamferData = fillet_chamfer_values.data(); std::vector<Geom::Point> result; - std::vector<Geom::Path> original_pathv_processed = pathv_to_linear_and_cubic_beziers(original_pathv); + Geom::PathVector original_pathv_processed = pathv_to_linear_and_cubic_beziers(original_pathv); int counter = 0; for (PathVector::const_iterator path_it = original_pathv_processed.begin(); path_it != original_pathv_processed.end(); ++path_it) { int pathCounter = 0; @@ -444,9 +441,7 @@ void LPEFilletChamfer::doBeforeEffect(SPLPEItem const *lpeItem) } else { fillet_chamfer_values.set_helper_size(helper_size); } - fillet_chamfer_values.set_document_unit(defaultUnit); fillet_chamfer_values.set_use_distance(use_knot_distance); - fillet_chamfer_values.set_unit(unit.get_abbreviation()); SPCurve *c = SP_IS_PATH(lpeItem) ? static_cast<SPPath const *>(lpeItem) ->get_original_curve() : SP_SHAPE(lpeItem)->getCurve(); @@ -466,7 +461,7 @@ int LPEFilletChamfer::getKnotsNumber(SPCurve const *c) { int nKnots = c->nodes_in_path(); PathVector const pv = pathv_to_linear_and_cubic_beziers(c->get_pathvector()); - for (std::vector<Geom::Path>::const_iterator path_it = pv.begin(); + for (Geom::PathVector::const_iterator path_it = pv.begin(); path_it != pv.end(); ++path_it) { if (!(*path_it).closed()) { nKnots--; @@ -476,17 +471,17 @@ int LPEFilletChamfer::getKnotsNumber(SPCurve const *c) } void -LPEFilletChamfer::adjustForNewPath(std::vector<Geom::Path> const &path_in) +LPEFilletChamfer::adjustForNewPath(Geom::PathVector const &path_in) { if (!path_in.empty()) { fillet_chamfer_values.recalculate_controlpoints_for_new_pwd2(pathv_to_linear_and_cubic_beziers(path_in)[0].toPwSb()); } } -std::vector<Geom::Path> -LPEFilletChamfer::doEffect_path(std::vector<Geom::Path> const &path_in) +Geom::PathVector +LPEFilletChamfer::doEffect_path(Geom::PathVector const &path_in) { - std::vector<Geom::Path> pathvector_out; + Geom::PathVector pathvector_out; Piecewise<D2<SBasis> > pwd2_in = paths_to_pw(pathv_to_linear_and_cubic_beziers(path_in)); pwd2_in = remove_short_cuts(pwd2_in, .01); Piecewise<D2<SBasis> > der = derivative(pwd2_in); @@ -495,7 +490,7 @@ LPEFilletChamfer::doEffect_path(std::vector<Geom::Path> const &path_in) std::vector<Point> filletChamferData = fillet_chamfer_values.data(); unsigned int counter = 0; const double K = (4.0 / 3.0) * (sqrt(2.0) - 1.0); - std::vector<Geom::Path> path_in_processed = pathv_to_linear_and_cubic_beziers(path_in); + Geom::PathVector path_in_processed = pathv_to_linear_and_cubic_beziers(path_in); for (PathVector::const_iterator path_it = path_in_processed.begin(); path_it != path_in_processed.end(); ++path_it) { if (path_it->empty()) @@ -548,7 +543,7 @@ LPEFilletChamfer::doEffect_path(std::vector<Geom::Path> const &path_in) ray2.setPoints(endArcPoint, (*cubic2)[1]); } Point handle2 = endArcPoint - Point::polar(ray2.angle(),k2); - bool ccwToggle = cross(curve_it1->finalPoint() - startArcPoint, endArcPoint - startArcPoint) < 0; + bool ccwToggle = cross(curve_it1->finalPoint() - startArcPoint, endArcPoint - startArcPoint) > 0; double angle = angle_between(ray1, ray2, ccwToggle); double handleAngle = ray1.angle() - angle; if (ccwToggle) { @@ -587,7 +582,7 @@ LPEFilletChamfer::doEffect_path(std::vector<Geom::Path> const &path_in) Geom::Path path_chamfer; path_chamfer.start(path_out.finalPoint()); if((is_straight_curve(*curve_it1) && is_straight_curve(*curve_it2Fixed) && method != FM_BEZIER )|| method == FM_ARC){ - path_chamfer.appendNew<SVGEllipticalArc>(rx, ry, angleArc, 0, ccwToggle, endArcPoint); + path_chamfer.appendNew<EllipticalArc>(rx, ry, angleArc, 0, ccwToggle, endArcPoint); } else { path_chamfer.appendNew<Geom::CubicBezier>(handle1, handle2, endArcPoint); } @@ -603,7 +598,7 @@ LPEFilletChamfer::doEffect_path(std::vector<Geom::Path> const &path_in) path_chamfer.start(path_out.finalPoint()); if((is_straight_curve(*curve_it1) && is_straight_curve(*curve_it2Fixed) && method != FM_BEZIER )|| method == FM_ARC){ ccwToggle = ccwToggle?0:1; - path_chamfer.appendNew<SVGEllipticalArc>(rx, ry, angleArc, 0, ccwToggle, endArcPoint); + path_chamfer.appendNew<EllipticalArc>(rx, ry, angleArc, 0, ccwToggle, endArcPoint); }else{ path_chamfer.appendNew<Geom::CubicBezier>(inverseHandle1, inverseHandle2, endArcPoint); } @@ -616,13 +611,13 @@ LPEFilletChamfer::doEffect_path(std::vector<Geom::Path> const &path_in) } else if (type == 2) { if((is_straight_curve(*curve_it1) && is_straight_curve(*curve_it2Fixed) && method != FM_BEZIER )|| method == FM_ARC){ ccwToggle = ccwToggle?0:1; - path_out.appendNew<SVGEllipticalArc>(rx, ry, angleArc, 0, ccwToggle, endArcPoint); + path_out.appendNew<EllipticalArc>(rx, ry, angleArc, 0, ccwToggle, endArcPoint); }else{ path_out.appendNew<Geom::CubicBezier>(inverseHandle1, inverseHandle2, endArcPoint); } } else if (type == 1){ if((is_straight_curve(*curve_it1) && is_straight_curve(*curve_it2Fixed) && method != FM_BEZIER )|| method == FM_ARC){ - path_out.appendNew<SVGEllipticalArc>(rx, ry, angleArc, 0, ccwToggle, endArcPoint); + path_out.appendNew<EllipticalArc>(rx, ry, angleArc, 0, ccwToggle, endArcPoint); } else { path_out.appendNew<Geom::CubicBezier>(handle1, handle2, endArcPoint); } diff --git a/src/live_effects/lpe-fillet-chamfer.h b/src/live_effects/lpe-fillet-chamfer.h index 0d6a1ff17..290a37f92 100644 --- a/src/live_effects/lpe-fillet-chamfer.h +++ b/src/live_effects/lpe-fillet-chamfer.h @@ -15,14 +15,6 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ -#if HAVE_CONFIG_H -# include "config.h" -#endif - -#if defined(GLIBMM_DISABLE_DEPRECATED) && defined(HAVE_GLIBMM_THREADS_H) -# include <glibmm/threads.h> -#endif - #include "live_effects/parameter/enum.h" #include "live_effects/parameter/bool.h" #include "live_effects/parameter/unit.h" @@ -45,11 +37,11 @@ public: LPEFilletChamfer(LivePathEffectObject *lpeobject); virtual ~LPEFilletChamfer(); - virtual std::vector<Geom::Path> doEffect_path(std::vector<Geom::Path> const &path_in); + virtual Geom::PathVector doEffect_path(Geom::PathVector const &path_in); virtual void doOnApply(SPLPEItem const *lpeItem); virtual void doBeforeEffect(SPLPEItem const *lpeItem); - virtual void adjustForNewPath(std::vector<Geom::Path> const &path_in); + virtual void adjustForNewPath(Geom::PathVector const &path_in); virtual Gtk::Widget* newWidget(); int getKnotsNumber(SPCurve const *c); @@ -61,8 +53,8 @@ public: void fillet(); void inverseFillet(); void updateFillet(); - void doUpdateFillet(std::vector<Geom::Path> const& original_pathv, double power); - void doChangeType(std::vector<Geom::Path> const& original_pathv, int type); + void doUpdateFillet(Geom::PathVector const& original_pathv, double power); + void doChangeType(Geom::PathVector const& original_pathv, int type); void refreshKnots(); FilletChamferPointArrayParam fillet_chamfer_values; @@ -74,7 +66,6 @@ private: BoolParam only_selected; BoolParam flexible; BoolParam use_knot_distance; - UnitParam unit; EnumParam<FilletMethod> method; ScalarParam radius; ScalarParam chamfer_steps; diff --git a/src/live_effects/lpe-gears.cpp b/src/live_effects/lpe-gears.cpp index 003e22567..d4d695542 100644 --- a/src/live_effects/lpe-gears.cpp +++ b/src/live_effects/lpe-gears.cpp @@ -168,7 +168,7 @@ Geom::Path Gear::path() { D2<SBasis> root = _arc(cursor, cursor+root_advance, root_radius()); makeContinuous(root, prev); pb.append(SBasisCurve(root)); - cursor += root_advance; + //cursor += root_advance; prev = root.at1(); if (base_radius() > root_radius()) { @@ -232,10 +232,10 @@ LPEGears::~LPEGears() } -std::vector<Geom::Path> -LPEGears::doEffect_path (std::vector<Geom::Path> const & path_in) +Geom::PathVector +LPEGears::doEffect_path (Geom::PathVector const &path_in) { - std::vector<Geom::Path> path_out; + Geom::PathVector path_out; Geom::Path gearpath = path_in[0]; Geom::Path::iterator it(gearpath.begin()); diff --git a/src/live_effects/lpe-gears.h b/src/live_effects/lpe-gears.h index bd5e4c4f9..5dd6dd239 100644 --- a/src/live_effects/lpe-gears.h +++ b/src/live_effects/lpe-gears.h @@ -22,7 +22,7 @@ public: LPEGears(LivePathEffectObject *lpeobject); virtual ~LPEGears(); - virtual std::vector<Geom::Path> doEffect_path (std::vector<Geom::Path> const & path_in); + virtual Geom::PathVector doEffect_path(Geom::PathVector const &path_in); private: ScalarParam teeth; diff --git a/src/live_effects/lpe-interpolate.cpp b/src/live_effects/lpe-interpolate.cpp index e41bc6f0e..b1ad07d23 100644 --- a/src/live_effects/lpe-interpolate.cpp +++ b/src/live_effects/lpe-interpolate.cpp @@ -61,7 +61,7 @@ LPEInterpolate::doEffect_path (Geom::PathVector const & path_in) return path_in; } - std::vector<Geom::Path> path_out; + Geom::PathVector path_out; Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_A = path_in[0].toPwSb(); Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_B = path_in[1].toPwSb(); diff --git a/src/live_effects/lpe-interpolate.h b/src/live_effects/lpe-interpolate.h index 679001a81..ef8043e46 100644 --- a/src/live_effects/lpe-interpolate.h +++ b/src/live_effects/lpe-interpolate.h @@ -27,7 +27,7 @@ public: LPEInterpolate(LivePathEffectObject *lpeobject); virtual ~LPEInterpolate(); - virtual std::vector<Geom::Path> doEffect_path (std::vector<Geom::Path> const & path_in); + virtual Geom::PathVector doEffect_path (Geom::PathVector const & path_in); virtual void resetDefaults(SPItem const* item); private: diff --git a/src/live_effects/lpe-interpolate_points.h b/src/live_effects/lpe-interpolate_points.h index 7a3364747..eb706a320 100644 --- a/src/live_effects/lpe-interpolate_points.h +++ b/src/live_effects/lpe-interpolate_points.h @@ -25,7 +25,7 @@ public: LPEInterpolatePoints(LivePathEffectObject *lpeobject); virtual ~LPEInterpolatePoints(); - virtual std::vector<Geom::Path> doEffect_path (std::vector<Geom::Path> const & path_in); + virtual Geom::PathVector doEffect_path (Geom::PathVector const & path_in); private: EnumParam<unsigned> interpolator_type; diff --git a/src/live_effects/lpe-jointype.cpp b/src/live_effects/lpe-jointype.cpp index 0111a0f99..28f99eb94 100644 --- a/src/live_effects/lpe-jointype.cpp +++ b/src/live_effects/lpe-jointype.cpp @@ -20,7 +20,7 @@ #include "display/curve.h" #include <2geom/path.h> -#include <2geom/svg-elliptical-arc.h> +#include <2geom/elliptical-arc.h> #include "lpe-jointype.h" @@ -110,6 +110,7 @@ void LPEJoinType::doOnApply(SPLPEItem const* lpeitem) sp_repr_css_attr_unref (css); line_width.param_set_value(width); + line_width.write_to_SVG(); } } @@ -150,7 +151,7 @@ void LPEJoinType::doOnRemove(SPLPEItem const* lpeitem) } } -std::vector<Geom::Path> LPEJoinType::doEffect_path(std::vector<Geom::Path> const & path_in) +Geom::PathVector LPEJoinType::doEffect_path(Geom::PathVector const & path_in) { Geom::PathVector ret; for (size_t i = 0; i < path_in.size(); ++i) { diff --git a/src/live_effects/lpe-jointype.h b/src/live_effects/lpe-jointype.h index bca0961c9..fddaeb619 100644 --- a/src/live_effects/lpe-jointype.h +++ b/src/live_effects/lpe-jointype.h @@ -24,7 +24,7 @@ public: virtual void doOnApply(SPLPEItem const* lpeitem);
virtual void doOnRemove(SPLPEItem const* lpeitem);
- virtual std::vector <Geom::Path> doEffect_path (std::vector<Geom::Path> const & path_in);
+ virtual Geom::PathVector doEffect_path (Geom::PathVector const & path_in);
private:
LPEJoinType(const LPEJoinType&);
diff --git a/src/live_effects/lpe-knot.cpp b/src/live_effects/lpe-knot.cpp index 820221abf..c3000fe0d 100644 --- a/src/live_effects/lpe-knot.cpp +++ b/src/live_effects/lpe-knot.cpp @@ -155,7 +155,7 @@ findShadowedTime(Geom::Path const &patha, std::vector<Geom::Point> const &pt_and // -for each component, the time at which this crossing occurs + the order of this crossing along the component (when starting from 0). namespace LPEKnotNS {//just in case... -CrossingPoints::CrossingPoints(std::vector<Geom::Path> const &paths) : std::vector<CrossingPoint>(){ +CrossingPoints::CrossingPoints(Geom::PathVector const &paths) : std::vector<CrossingPoint>(){ // std::cout<<"\nCrossingPoints creation from path vector\n"; for( unsigned i=0; i<paths.size(); i++){ for( unsigned ii=0; ii < size_nondegenerate(paths[i]); ii++){ @@ -391,11 +391,11 @@ LPEKnot::updateSwitcher(){ } } -std::vector<Geom::Path> -LPEKnot::doEffect_path (std::vector<Geom::Path> const &path_in) +Geom::PathVector +LPEKnot::doEffect_path (Geom::PathVector const &path_in) { using namespace Geom; - std::vector<Geom::Path> path_out; + Geom::PathVector path_out; if (gpaths.size()==0){ return path_in; @@ -433,7 +433,7 @@ LPEKnot::doEffect_path (std::vector<Geom::Path> const &path_in) std::vector<Point> flag_j = gpaths[j][curveidx].pointAndDerivatives(t,1); - int geom_sign = ( cross(flag_i[1],flag_j[1]) > 0 ? 1 : -1); + int geom_sign = ( cross(flag_i[1], flag_j[1]) < 0 ? 1 : -1); bool i0_is_under = false; if ( crossing_points[p].sign * geom_sign > 0 ){ @@ -486,8 +486,9 @@ LPEKnot::doEffect_path (std::vector<Geom::Path> const &path_in) ++beg_comp; --end_comp; Path first = gpaths[i0].portion(dom.back()); - //FIXME: STITCH_DISCONTINUOUS should not be necessary (?!?) - first.append(gpaths[i0].portion(dom.front()), Path::STITCH_DISCONTINUOUS); + //FIXME: stitching should not be necessary (?!?) + first.setStitching(true); + first.append(gpaths[i0].portion(dom.front())); path_out.push_back(first); } } @@ -503,11 +504,11 @@ LPEKnot::doEffect_path (std::vector<Geom::Path> const &path_in) //recursively collect gpaths and stroke widths (stolen from "sp-lpe_item.cpp"). static void -collectPathsAndWidths (SPLPEItem const *lpeitem, std::vector<Geom::Path> &paths, std::vector<double> &stroke_widths){ +collectPathsAndWidths (SPLPEItem const *lpeitem, Geom::PathVector &paths, std::vector<double> &stroke_widths){ if (SP_IS_GROUP(lpeitem)) { - GSList const *item_list = sp_item_group_item_list(SP_GROUP(lpeitem)); - for ( GSList const *iter = item_list; iter; iter = iter->next ) { - SPObject *subitem = static_cast<SPObject *>(iter->data); + std::vector<SPItem*> item_list = sp_item_group_item_list(SP_GROUP(lpeitem)); + for ( std::vector<SPItem*>::const_iterator iter = item_list.begin(); iter != item_list.end(); iter++) { + SPObject *subitem = *iter; if (SP_IS_LPE_ITEM(subitem)) { collectPathsAndWidths(SP_LPE_ITEM(subitem), paths, stroke_widths); } @@ -615,8 +616,7 @@ LPEKnot::addCanvasIndicators(SPLPEItem const */*lpeitem*/, std::vector<Geom::Pat svgd = "M 10,0 C 10,5.52 5.52,10 0,10 -5.52,10 -10,5.52 -10,0 c 0,-5.52 4.48,-10 10,-10 5.52,0 10,4.48 10,10 z"; } PathVector pathv = sp_svg_read_pathv(svgd); - pathv *= Affine(r,0,0,r,0,0); - pathv += switcher; + pathv *= Affine(r,0,0,r,0,0) * Translate(switcher); hp_vec.push_back(pathv); } diff --git a/src/live_effects/lpe-knot.h b/src/live_effects/lpe-knot.h index 080f32de2..95bfaf6e1 100644 --- a/src/live_effects/lpe-knot.h +++ b/src/live_effects/lpe-knot.h @@ -42,8 +42,8 @@ struct CrossingPoint { class CrossingPoints : public std::vector<CrossingPoint>{ public: CrossingPoints() : std::vector<CrossingPoint>() {} - CrossingPoints(Geom::CrossingSet const &cs, std::vector<Geom::Path> const &path);//for self crossings only! - CrossingPoints(std::vector<Geom::Path> const &paths); + CrossingPoints(Geom::CrossingSet const &cs, Geom::PathVector const &path);//for self crossings only! + CrossingPoints(Geom::PathVector const &paths); CrossingPoints(std::vector<double> const &input); std::vector<double> to_vector(); CrossingPoint get(unsigned const i, unsigned const ni); @@ -57,7 +57,7 @@ public: virtual ~LPEKnot(); virtual void doBeforeEffect (SPLPEItem const* lpeitem); - virtual std::vector<Geom::Path> doEffect_path (std::vector<Geom::Path> const & input_path); + virtual Geom::PathVector doEffect_path (Geom::PathVector const & input_path); /* the knotholder entity classes must be declared friends */ friend class KnotHolderEntityCrossingSwitcher; @@ -65,7 +65,7 @@ public: protected: virtual void addCanvasIndicators(SPLPEItem const *lpeitem, std::vector<Geom::PathVector> &hp_vec); - std::vector<Geom::Path> supplied_path; //for knotholder business + Geom::PathVector supplied_path; //for knotholder business private: void updateSwitcher(); @@ -79,7 +79,7 @@ private: LPEKnotNS::CrossingPoints crossing_points;//topology representation of the knot. - std::vector<Geom::Path> gpaths;//the collection of all the paths in the object or group. + Geom::PathVector gpaths;//the collection of all the paths in the object or group. std::vector<double> gstroke_widths;//the collection of all the stroke widths in the object or group. //UI: please, someone, help me to improve this!! diff --git a/src/live_effects/lpe-lattice2.cpp b/src/live_effects/lpe-lattice2.cpp index abd6e7786..8c7f46cbd 100644 --- a/src/live_effects/lpe-lattice2.cpp +++ b/src/live_effects/lpe-lattice2.cpp @@ -18,6 +18,8 @@ #include "live_effects/lpe-lattice2.h" +#include <gtkmm/expander.h> + #include "sp-shape.h" #include "sp-item.h" #include "sp-path.h" @@ -289,7 +291,7 @@ LPELattice2::vertical(PointParam ¶m_one, PointParam ¶m_two, Geom::Line v double Y = (A[Geom::Y] + B[Geom::Y])/2; A[Geom::Y] = Y; B[Geom::Y] = Y; - Geom::Point nearest = vert.pointAt(vert.nearestPoint(A)); + Geom::Point nearest = vert.pointAt(vert.nearestTime(A)); double distance_one = Geom::distance(A,nearest); double distance_two = Geom::distance(B,nearest); double distance_middle = (distance_one + distance_two)/2; @@ -310,7 +312,7 @@ LPELattice2::horizontal(PointParam ¶m_one, PointParam ¶m_two, Geom::Line double X = (A[Geom::X] + B[Geom::X])/2; A[Geom::X] = X; B[Geom::X] = X; - Geom::Point nearest = horiz.pointAt(horiz.nearestPoint(A)); + Geom::Point nearest = horiz.pointAt(horiz.nearestTime(A)); double distance_one = Geom::distance(A,nearest); double distance_two = Geom::distance(B,nearest); double distance_middle = (distance_one + distance_two)/2; diff --git a/src/live_effects/lpe-lattice2.h b/src/live_effects/lpe-lattice2.h index ff2e75641..b32903c9e 100644 --- a/src/live_effects/lpe-lattice2.h +++ b/src/live_effects/lpe-lattice2.h @@ -18,12 +18,16 @@ * * Released under GNU GPL, read the file 'COPYING' for more information */ -#include <gtkmm.h> #include "live_effects/parameter/enum.h" +#include <gtkmm/widget.h> #include "live_effects/effect.h" #include "live_effects/parameter/point.h" #include "live_effects/lpegroupbbox.h" +namespace Gtk { +class Expander; +} + namespace Inkscape { namespace LivePathEffect { diff --git a/src/live_effects/lpe-line_segment.cpp b/src/live_effects/lpe-line_segment.cpp index 78d286143..dfd8aea8f 100644 --- a/src/live_effects/lpe-line_segment.cpp +++ b/src/live_effects/lpe-line_segment.cpp @@ -48,16 +48,16 @@ LPELineSegment::doBeforeEffect (SPLPEItem const* lpeitem) Inkscape::UI::Tools::lpetool_get_limiting_bbox_corners(lpeitem->document, bboxA, bboxB); } -std::vector<Geom::Path> -LPELineSegment::doEffect_path (std::vector<Geom::Path> const & path_in) +Geom::PathVector +LPELineSegment::doEffect_path (Geom::PathVector const & path_in) { - std::vector<Geom::Path> output; + Geom::PathVector output; - A = initialPoint(path_in); - B = finalPoint(path_in); + A = path_in.initialPoint(); + B = path_in.finalPoint(); Geom::Rect dummyRect(bboxA, bboxB); - boost::optional<Geom::LineSegment> intersection_segment = Geom::rect_line_intersect(dummyRect, Geom::Line(A, B)); + boost::optional<Geom::LineSegment> intersection_segment = Geom::Line(A, B).clip(dummyRect); if (!intersection_segment) { g_print ("Possible error - no intersection with limiting bounding box.\n"); @@ -65,11 +65,11 @@ LPELineSegment::doEffect_path (std::vector<Geom::Path> const & path_in) } if (end_type == END_OPEN_INITIAL || end_type == END_OPEN_BOTH) { - A = (*intersection_segment).initialPoint(); + A = intersection_segment->initialPoint(); } if (end_type == END_OPEN_FINAL || end_type == END_OPEN_BOTH) { - B = (*intersection_segment).finalPoint(); + B = intersection_segment->finalPoint(); } Geom::Path path(A); diff --git a/src/live_effects/lpe-line_segment.h b/src/live_effects/lpe-line_segment.h index 1a57688b6..def828fe2 100644 --- a/src/live_effects/lpe-line_segment.h +++ b/src/live_effects/lpe-line_segment.h @@ -34,7 +34,7 @@ public: virtual void doBeforeEffect (SPLPEItem const* lpeitem); - virtual std::vector<Geom::Path> doEffect_path (std::vector<Geom::Path> const & path_in); + virtual Geom::PathVector doEffect_path (Geom::PathVector const & path_in); //private: EnumParam<EndType> end_type; diff --git a/src/live_effects/lpe-mirror_symmetry.cpp b/src/live_effects/lpe-mirror_symmetry.cpp index 0bb67a4a2..783053df2 100644 --- a/src/live_effects/lpe-mirror_symmetry.cpp +++ b/src/live_effects/lpe-mirror_symmetry.cpp @@ -64,24 +64,24 @@ LPEMirrorSymmetry::doOnApply (SPLPEItem const* lpeitem) Point B(bbox.left(), bbox.top()); A *= t; B *= t; - Piecewise<D2<SBasis> > rline = Piecewise<D2<SBasis> >(D2<SBasis>(Linear(A[X], B[X]), Linear(A[Y], B[Y]))); + Piecewise<D2<SBasis> > rline = Piecewise<D2<SBasis> >(D2<SBasis>(SBasis(A[X], B[X]), SBasis(A[Y], B[Y]))); reflection_line.set_new_value(rline, true); } -std::vector<Geom::Path> -LPEMirrorSymmetry::doEffect_path (std::vector<Geom::Path> const & path_in) +Geom::PathVector +LPEMirrorSymmetry::doEffect_path (Geom::PathVector const & path_in) { // Don't allow empty path parameter: if ( reflection_line.get_pathvector().empty() ) { return path_in; } - std::vector<Geom::Path> path_out; + Geom::PathVector path_out; if (!discard_orig_path) { path_out = path_in; } - std::vector<Geom::Path> mline(reflection_line.get_pathvector()); + Geom::PathVector mline(reflection_line.get_pathvector()); Geom::Point A(mline.front().initialPoint()); Geom::Point B(mline.back().finalPoint()); diff --git a/src/live_effects/lpe-mirror_symmetry.h b/src/live_effects/lpe-mirror_symmetry.h index a4a2b86c0..7a484a473 100644 --- a/src/live_effects/lpe-mirror_symmetry.h +++ b/src/live_effects/lpe-mirror_symmetry.h @@ -32,7 +32,7 @@ public: virtual void doBeforeEffect (SPLPEItem const* lpeitem); - virtual std::vector<Geom::Path> doEffect_path (std::vector<Geom::Path> const & path_in); + virtual Geom::PathVector doEffect_path (Geom::PathVector const & path_in); private: BoolParam discard_orig_path; diff --git a/src/live_effects/lpe-offset.cpp b/src/live_effects/lpe-offset.cpp index ba7179476..dd7af92a2 100644 --- a/src/live_effects/lpe-offset.cpp +++ b/src/live_effects/lpe-offset.cpp @@ -20,7 +20,7 @@ #include <2geom/path.h> #include <2geom/piecewise.h> #include <2geom/sbasis-geometric.h> -#include <2geom/svg-elliptical-arc.h> +#include <2geom/elliptical-arc.h> #include <2geom/transforms.h> namespace Inkscape { @@ -52,7 +52,7 @@ static void append_half_circle(Geom::Piecewise<Geom::D2<Geom::SBasis> > &pwd2, using namespace Geom; double r = L2(dir); - SVGEllipticalArc cap(center + dir, r, r, angle_between(Point(1,0), dir), false, false, center - dir); + EllipticalArc cap(center + dir, r, r, angle_between(Point(1,0), dir), false, false, center - dir); Piecewise<D2<SBasis> > cap_pwd2(cap.toSBasis()); pwd2.continuousConcat(cap_pwd2); } @@ -72,7 +72,7 @@ LPEOffset::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_ Piecewise<D2<SBasis> > output; - double t = nearest_point(offset_pt, pwd2_in); + double t = nearest_time(offset_pt, pwd2_in); Point A = pwd2_in.valueAt(t); double offset = L2(A - offset_pt); diff --git a/src/live_effects/lpe-parallel.cpp b/src/live_effects/lpe-parallel.cpp index aa7405607..23cd5e2e7 100644 --- a/src/live_effects/lpe-parallel.cpp +++ b/src/live_effects/lpe-parallel.cpp @@ -91,7 +91,7 @@ LPEParallel::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd C = offset_pt - dir * length_left; D = offset_pt + dir * length_right; - output = Piecewise<D2<SBasis> >(D2<SBasis>(Linear(C[X], D[X]), Linear(C[Y], D[Y]))); + output = Piecewise<D2<SBasis> >(D2<SBasis>(SBasis(C[X], D[X]), SBasis(C[Y], D[Y]))); return output + dir; } diff --git a/src/live_effects/lpe-perp_bisector.cpp b/src/live_effects/lpe-perp_bisector.cpp index feed55b26..660318c57 100644 --- a/src/live_effects/lpe-perp_bisector.cpp +++ b/src/live_effects/lpe-perp_bisector.cpp @@ -67,7 +67,7 @@ KnotHolderEntityEnd::bisector_end_set(Geom::Point const &p, guint state, bool le Geom::Point const s = snap_knot_position(p, state); - double lambda = Geom::nearest_point(s, lpe->M, lpe->perp_dir); + double lambda = Geom::nearest_time(s, lpe->M, lpe->perp_dir); if (left) { lpe->C = lpe->M + lpe->perp_dir * lambda; lpe->length_left.param_set_value(lambda); @@ -146,7 +146,7 @@ LPEPerpBisector::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & C = M + perp_dir * length_left; D = M - perp_dir * length_right; - output = Piecewise<D2<SBasis> >(D2<SBasis>(Linear(C[X], D[X]), Linear(C[Y], D[Y]))); + output = Piecewise<D2<SBasis> >(D2<SBasis>(SBasis(C[X], D[X]), SBasis(C[Y], D[Y]))); return output; } diff --git a/src/live_effects/lpe-perspective-envelope.cpp b/src/live_effects/lpe-perspective-envelope.cpp index 4eac2d6eb..72c1b0e99 100644 --- a/src/live_effects/lpe-perspective-envelope.cpp +++ b/src/live_effects/lpe-perspective-envelope.cpp @@ -40,7 +40,8 @@ static const Util::EnumDataConverter<unsigned> DeformationTypeConverter(Deformat LPEPerspectiveEnvelope::LPEPerspectiveEnvelope(LivePathEffectObject *lpeobject) : Effect(lpeobject), - // initialise your parameters here: + horizontal_mirror(_("Mirror movements in horizontal"), _("Mirror movements in horizontal"), "horizontal_mirror", &wr, this, false), + vertical_mirror(_("Mirror movements in vertical"), _("Mirror movements in vertical"), "vertical_mirror", &wr, this, false), deform_type(_("Type"), _("Select the type of deformation"), "deform_type", DeformationTypeConverter, &wr, this, DEFORMATION_PERSPECTIVE), up_left_point(_("Top Left"), _("Top Left - <b>Ctrl+Alt+Click</b>: reset, <b>Ctrl</b>: move along axes"), "up_left_point", &wr, this), up_right_point(_("Top Right"), _("Top Right - <b>Ctrl+Alt+Click</b>: reset, <b>Ctrl</b>: move along axes"), "up_right_point", &wr, this), @@ -49,6 +50,8 @@ LPEPerspectiveEnvelope::LPEPerspectiveEnvelope(LivePathEffectObject *lpeobject) { // register all your parameters here, so Inkscape knows which parameters this effect has: registerParameter(&deform_type); + registerParameter(&horizontal_mirror); + registerParameter(&vertical_mirror); registerParameter(&up_left_point); registerParameter(&up_right_point); registerParameter(&down_left_point); @@ -63,6 +66,13 @@ void LPEPerspectiveEnvelope::doEffect(SPCurve *curve) { using Geom::X; using Geom::Y; + if(are_near(up_left_point, up_right_point) && + are_near(up_right_point, down_left_point) && + are_near(down_left_point, down_right_point)) { + g_warning("Perspective/Envelope LPE::doBeforeEffect - lpeobj with invalid parameter, the same value in 4 handles!"); + resetGrid(); + return; + } double projmatrix[3][3]; if(deform_type == DEFORMATION_PERSPECTIVE) { std::vector<Geom::Point> handles(4); @@ -306,9 +316,61 @@ LPEPerspectiveEnvelope::newWidget() } void +LPEPerspectiveEnvelope::vertical(PointParam ¶m_one, PointParam ¶m_two, Geom::Line vert) +{ + Geom::Point A = param_one; + Geom::Point B = param_two; + double Y = (A[Geom::Y] + B[Geom::Y])/2; + A[Geom::Y] = Y; + B[Geom::Y] = Y; + Geom::Point nearest = vert.pointAt(vert.nearestTime(A)); + double distance_one = Geom::distance(A,nearest); + double distance_two = Geom::distance(B,nearest); + double distance_middle = (distance_one + distance_two)/2; + if(A[Geom::X] > B[Geom::X]) { + distance_middle *= -1; + } + A[Geom::X] = nearest[Geom::X] - distance_middle; + B[Geom::X] = nearest[Geom::X] + distance_middle; + param_one.param_setValue(A, true); + param_two.param_setValue(B, true); +} + +void +LPEPerspectiveEnvelope::horizontal(PointParam ¶m_one, PointParam ¶m_two, Geom::Line horiz) +{ + Geom::Point A = param_one; + Geom::Point B = param_two; + double X = (A[Geom::X] + B[Geom::X])/2; + A[Geom::X] = X; + B[Geom::X] = X; + Geom::Point nearest = horiz.pointAt(horiz.nearestTime(A)); + double distance_one = Geom::distance(A,nearest); + double distance_two = Geom::distance(B,nearest); + double distance_middle = (distance_one + distance_two)/2; + if(A[Geom::Y] > B[Geom::Y]) { + distance_middle *= -1; + } + A[Geom::Y] = nearest[Geom::Y] - distance_middle; + B[Geom::Y] = nearest[Geom::Y] + distance_middle; + param_one.param_setValue(A, true); + param_two.param_setValue(B, true); +} + +void LPEPerspectiveEnvelope::doBeforeEffect (SPLPEItem const* lpeitem) { original_bbox(lpeitem); + Geom::Line vert(Geom::Point(boundingbox_X.middle(),boundingbox_Y.max()), Geom::Point(boundingbox_X.middle(), boundingbox_Y.min())); + Geom::Line horiz(Geom::Point(boundingbox_X.min(),boundingbox_Y.middle()), Geom::Point(boundingbox_X.max(), boundingbox_Y.middle())); + if(vertical_mirror) { + vertical(up_left_point, up_right_point,vert); + vertical(down_left_point, down_right_point,vert); + } + if(horizontal_mirror) { + horizontal(up_left_point, down_left_point,horiz); + horizontal(up_right_point, down_right_point,horiz); + } SPLPEItem * item = const_cast<SPLPEItem*>(lpeitem); item->apply_to_clippath(item); item->apply_to_mask(item); diff --git a/src/live_effects/lpe-perspective-envelope.h b/src/live_effects/lpe-perspective-envelope.h index e25f059a3..dd14dc212 100644 --- a/src/live_effects/lpe-perspective-envelope.h +++ b/src/live_effects/lpe-perspective-envelope.h @@ -42,6 +42,10 @@ public: virtual void resetDefaults(SPItem const* item); + virtual void vertical(PointParam ¶mA,PointParam ¶mB, Geom::Line vert); + + virtual void horizontal(PointParam ¶mA,PointParam ¶mB,Geom::Line horiz); + virtual void doBeforeEffect(SPLPEItem const* lpeitem); virtual Gtk::Widget * newWidget(); @@ -54,6 +58,8 @@ protected: void addCanvasIndicators(SPLPEItem const */*lpeitem*/, std::vector<Geom::PathVector> &hp_vec); private: + BoolParam horizontal_mirror; + BoolParam vertical_mirror; EnumParam<unsigned> deform_type; PointParam up_left_point; PointParam up_right_point; diff --git a/src/live_effects/lpe-powerstroke.cpp b/src/live_effects/lpe-powerstroke.cpp index 5d9d224e8..f90d67d4e 100644 --- a/src/live_effects/lpe-powerstroke.cpp +++ b/src/live_effects/lpe-powerstroke.cpp @@ -27,12 +27,13 @@ #include <2geom/sbasis-geometric.h> #include <2geom/transforms.h> #include <2geom/bezier-utils.h> -#include <2geom/svg-elliptical-arc.h> +#include <2geom/elliptical-arc.h> #include <2geom/sbasis-to-bezier.h> #include <2geom/path-sink.h> #include <2geom/path-intersection.h> #include <2geom/crossing.h> #include <2geom/ellipse.h> +#include <2geom/circle.h> #include <2geom/math-utils.h> #include <math.h> @@ -46,9 +47,9 @@ namespace Geom { static boost::optional<Point> intersection_point( Point const & origin_a, Point const & vector_a, Point const & origin_b, Point const & vector_b) { - Coord denom = cross(vector_b, vector_a); + Coord denom = cross(vector_a, vector_b); if (!are_near(denom,0.)){ - Coord t = (cross(origin_a,vector_b) + cross(vector_b,origin_b)) / denom; + Coord t = (cross(vector_b, origin_a) + cross(origin_b, vector_b)) / denom; return origin_a + t * vector_a; } return boost::none; @@ -94,71 +95,6 @@ static Ellipse find_ellipse(Point P, Point Q, Point O) } /** - * Refer to: Weisstein, Eric W. "Circle-Circle Intersection." - From MathWorld--A Wolfram Web Resource. - http://mathworld.wolfram.com/Circle-CircleIntersection.html - * - * @return 0 if no intersection - * @return 1 if one circle is contained in the other - * @return 2 if intersections are found (they are written to p0 and p1) - */ -static int circle_circle_intersection(Circle const &circle0, Circle const &circle1, - Point & p0, Point & p1) -{ - Point X0 = circle0.center(); - double r0 = circle0.ray(); - Point X1 = circle1.center(); - double r1 = circle1.ray(); - - /* dx and dy are the vertical and horizontal distances between - * the circle centers. - */ - Point D = X1 - X0; - - /* Determine the straight-line distance between the centers. */ - double d = L2(D); - - /* Check for solvability. */ - if (d > (r0 + r1)) - { - /* no solution. circles do not intersect. */ - return 0; - } - if (d <= fabs(r0 - r1)) - { - /* no solution. one circle is contained in the other */ - return 1; - } - - /* 'point 2' is the point where the line through the circle - * intersection points crosses the line between the circle - * centers. - */ - - /* Determine the distance from point 0 to point 2. */ - double a = ((r0*r0) - (r1*r1) + (d*d)) / (2.0 * d) ; - - /* Determine the coordinates of point 2. */ - Point p2 = X0 + D * (a/d); - - /* Determine the distance from point 2 to either of the - * intersection points. - */ - double h = std::sqrt((r0*r0) - (a*a)); - - /* Now determine the offsets of the intersection points from - * point 2. - */ - Point r = (h/d)*rot90(D); - - /* Determine the absolute intersection points. */ - p0 = p2 + r; - p1 = p2 - r; - - return 2; -} - -/** * Find circle that touches inside of the curve, with radius matching the curvature, at time value \c t. * Because this method internally uses unitTangentAt, t should be smaller than 1.0 (see unitTangentAt). */ @@ -313,7 +249,7 @@ LPEPowerStroke::doOnApply(SPLPEItem const* lpeitem) Geom::Path const &path = pathv.front(); Geom::Path::size_type const size = path.size_default(); if (!path.closed()) { - points.push_back( Geom::Point(0.2,width) ); + points.push_back( Geom::Point(0.2,width) ); } points.push_back( Geom::Point(0.5*size,width) ); if (!path.closed()) { @@ -362,7 +298,7 @@ void LPEPowerStroke::doOnRemove(SPLPEItem const* lpeitem) } void -LPEPowerStroke::adjustForNewPath(std::vector<Geom::Path> const & path_in) +LPEPowerStroke::adjustForNewPath(Geom::PathVector const & path_in) { if (!path_in.empty()) { offset_points.recalculate_controlpoints_for_new_pwd2(path_in[0].toPwSb()); @@ -387,6 +323,8 @@ static Geom::Path path_from_piecewise_fix_cusps( Geom::Piecewise<Geom::D2<Geom:: return pb.peek().front(); } + pb.setStitching(true); + Geom::Point start = B[0].at0(); pb.moveTo(start); build_from_sbasis(pb, B[0], tol, false); @@ -446,7 +384,7 @@ static Geom::Path path_from_piecewise_fix_cusps( Geom::Piecewise<Geom::D2<Geom:: break; } - pb.arcTo( ellipse.ray(Geom::X), ellipse.ray(Geom::Y), ellipse.rot_angle(), + pb.arcTo( ellipse.ray(Geom::X), ellipse.ray(Geom::Y), ellipse.rotationAngle(), false, width < 0, B[i].at0() ); break; @@ -482,24 +420,24 @@ static Geom::Path path_from_piecewise_fix_cusps( Geom::Piecewise<Geom::D2<Geom:: // Extrapolate using the curvature at the end of the path segments to join Geom::Circle circle1 = Geom::touching_circle(reverse(B[prev_i]), 0.0); Geom::Circle circle2 = Geom::touching_circle(B[i], 0.0); - Geom::Point points[2]; - int solutions = circle_circle_intersection(circle1, circle2, points[0], points[1]); - if (solutions == 2) { + std::vector<Geom::ShapeIntersection> solutions; + solutions = circle1.intersect(circle2); + if (solutions.size() == 2) { Geom::Point sol(0.,0.); - if ( dot(tang2,points[0]-B[i].at0()) > 0 ) { + if ( dot(tang2, solutions[0].point() - B[i].at0()) > 0 ) { // points[0] is bad, choose points[1] - sol = points[1]; - } else if ( dot(tang2,points[1]-B[i].at0()) > 0 ) { // points[0] could be good, now check points[1] + sol = solutions[1].point(); + } else if ( dot(tang2, solutions[1].point() - B[i].at0()) > 0 ) { // points[0] could be good, now check points[1] // points[1] is bad, choose points[0] - sol = points[0]; + sol = solutions[0].point(); } else { // both points are good, choose nearest - sol = ( distanceSq(B[i].at0(), points[0]) < distanceSq(B[i].at0(), points[1]) ) ? - points[0] : points[1]; + sol = ( distanceSq(B[i].at0(), solutions[0].point()) < distanceSq(B[i].at0(), solutions[1].point()) ) ? + solutions[0].point() : solutions[1].point(); } - Geom::EllipticalArc *arc0 = circle1.arc(B[prev_i].at1(), 0.5*(B[prev_i].at1()+sol), sol, true); - Geom::EllipticalArc *arc1 = circle2.arc(sol, 0.5*(sol+B[i].at0()), B[i].at0(), true); + Geom::EllipticalArc *arc0 = circle1.arc(B[prev_i].at1(), 0.5*(B[prev_i].at1()+sol), sol); + Geom::EllipticalArc *arc1 = circle2.arc(sol, 0.5*(sol+B[i].at0()), B[i].at0()); if (arc0) { build_from_sbasis(pb,arc0->toSBasis(), tol, false); @@ -574,7 +512,7 @@ static Geom::Path path_from_piecewise_fix_cusps( Geom::Piecewise<Geom::D2<Geom:: Geom::Path spiro; Spiro::spiro_run(controlpoints, 4, spiro); - pb.append(spiro.portion(1,spiro.size_open()-1), Geom::Path::STITCH_DISCONTINUOUS); + pb.append(spiro.portion(1, spiro.size_open() - 1)); break; } case LINEJOIN_BEVEL: @@ -593,15 +531,15 @@ static Geom::Path path_from_piecewise_fix_cusps( Geom::Piecewise<Geom::D2<Geom:: if (cross.size() != 1) { // empty crossing or too many crossings: default to bevel pb.lineTo(B[i].at0()); - pb.append(bzr2, Geom::Path::STITCH_DISCONTINUOUS); + pb.append(bzr2); } else { // :-) quick hack: for (unsigned i=0; i < bzr1.size_open(); ++i) { pb.backspace(); } - pb.append( bzr1.portion(0, cross[0].ta), Geom::Path::STITCH_DISCONTINUOUS ); - pb.append( bzr2.portion(cross[0].tb, bzr2.size_open()), Geom::Path::STITCH_DISCONTINUOUS ); + pb.append( bzr1.portion(0, cross[0].ta) ); + pb.append( bzr2.portion(cross[0].tb, bzr2.size_open()) ); } } } else { @@ -615,12 +553,12 @@ static Geom::Path path_from_piecewise_fix_cusps( Geom::Piecewise<Geom::D2<Geom:: } -std::vector<Geom::Path> -LPEPowerStroke::doEffect_path (std::vector<Geom::Path> const & path_in) +Geom::PathVector +LPEPowerStroke::doEffect_path (Geom::PathVector const & path_in) { using namespace Geom; - std::vector<Geom::Path> path_out; + Geom::PathVector path_out; if (path_in.empty()) { return path_out; } @@ -737,12 +675,12 @@ LPEPowerStroke::doEffect_path (std::vector<Geom::Path> const & path_in) default: { double radius1 = 0.5 * distance(pwd2_out.lastValue(), mirrorpath.firstValue()); - fixed_path.appendNew<SVGEllipticalArc>( radius1, radius1, M_PI/2., false, y.lastValue() < 0, mirrorpath.firstValue() ); + fixed_path.appendNew<EllipticalArc>( radius1, radius1, M_PI/2., false, y.lastValue() < 0, mirrorpath.firstValue() ); break; } } - fixed_path.append(fixed_mirrorpath, Geom::Path::STITCH_DISCONTINUOUS); + fixed_path.append(fixed_mirrorpath); switch (start_linecap) { case LINECAP_ZERO_WIDTH: @@ -775,7 +713,7 @@ LPEPowerStroke::doEffect_path (std::vector<Geom::Path> const & path_in) default: { double radius2 = 0.5 * distance(pwd2_out.firstValue(), mirrorpath.lastValue()); - fixed_path.appendNew<SVGEllipticalArc>( radius2, radius2, M_PI/2., false, y.firstValue() < 0, pwd2_out.firstValue() ); + fixed_path.appendNew<EllipticalArc>( radius2, radius2, M_PI/2., false, y.firstValue() < 0, pwd2_out.firstValue() ); break; } } diff --git a/src/live_effects/lpe-powerstroke.h b/src/live_effects/lpe-powerstroke.h index a773434aa..135d39274 100644 --- a/src/live_effects/lpe-powerstroke.h +++ b/src/live_effects/lpe-powerstroke.h @@ -26,13 +26,13 @@ public: virtual ~LPEPowerStroke(); - virtual std::vector<Geom::Path> doEffect_path (std::vector<Geom::Path> const & path_in); + virtual Geom::PathVector doEffect_path (Geom::PathVector const & path_in); virtual void doOnApply(SPLPEItem const* lpeitem); virtual void doOnRemove(SPLPEItem const* lpeitem); // methods called by path-manipulator upon edits - void adjustForNewPath(std::vector<Geom::Path> const & path_in); + void adjustForNewPath(Geom::PathVector const & path_in); PowerStrokePointArrayParam offset_points; diff --git a/src/live_effects/lpe-rough-hatches.cpp b/src/live_effects/lpe-rough-hatches.cpp index 455653e46..76421e0f0 100644 --- a/src/live_effects/lpe-rough-hatches.cpp +++ b/src/live_effects/lpe-rough-hatches.cpp @@ -529,7 +529,8 @@ LPERoughHatches::smoothSnake(std::vector<std::vector<Point> > const &linearSnake } if ( fat_output.get_value() ){ res_comp = res_comp_bot; - res_comp.append(res_comp_top.reverse(),Geom::Path::STITCH_DISCONTINUOUS); + res_comp.setStitching(true); + res_comp.append(res_comp_top.reversed()); } result.concat(res_comp.toPwSb()); } diff --git a/src/live_effects/lpe-roughen.cpp b/src/live_effects/lpe-roughen.cpp index 9d1c3152b..33ffd96d6 100644 --- a/src/live_effects/lpe-roughen.cpp +++ b/src/live_effects/lpe-roughen.cpp @@ -20,7 +20,6 @@ #include "live_effects/parameter/parameter.h" #include "helper/geom.h" #include <glibmm/i18n.h> -#include <util/units.h> #include <cmath> namespace Inkscape { @@ -36,7 +35,6 @@ DMConverter(DivisionMethodData, DM_END); LPERoughen::LPERoughen(LivePathEffectObject *lpeobject) : Effect(lpeobject), // initialise your parameters here: - unit(_("Unit"), _("Unit"), "unit", &wr, this), method(_("Method"), _("Division method"), "method", DMConverter, &wr, this, DM_SEGMENTS), max_segment_size(_("Max. segment size"), _("Max. segment size"), @@ -54,7 +52,6 @@ LPERoughen::LPERoughen(LivePathEffectObject *lpeobject) shift_node_handles(_("Shift node handles"), _("Shift node handles"), "shift_node_handles", &wr, this, true) { - registerParameter(&unit); registerParameter(&method); registerParameter(&max_segment_size); registerParameter(&segments); @@ -98,14 +95,6 @@ Gtk::Widget *LPERoughen::newWidget() if ((*it)->widget_is_visible) { Parameter *param = *it; Gtk::Widget *widg = dynamic_cast<Gtk::Widget *>(param->param_newWidget()); - if (param->param_key == "unit") { - Gtk::Label *unit_label = Gtk::manage(new Gtk::Label( - Glib::ustring(_("<b>Roughen unit</b>")), Gtk::ALIGN_START)); - unit_label->set_use_markup(true); - vbox->pack_start(*unit_label, false, false, 2); - vbox->pack_start(*Gtk::manage(new Gtk::HSeparator()), - Gtk::PACK_EXPAND_WIDGET); - } if (param->param_key == "method") { Gtk::Label *method_label = Gtk::manage(new Gtk::Label( Glib::ustring(_("<b>Add nodes</b> Subdivide each segment")), @@ -160,11 +149,8 @@ double LPERoughen::sign(double random_number) Geom::Point LPERoughen::randomize() { - Inkscape::Util::Unit const *svg_units = SP_ACTIVE_DESKTOP->namedview->svg_units; - double displace_x_parsed = Inkscape::Util::Quantity::convert( - displace_x * global_randomize, unit.get_abbreviation(), svg_units->abbr); - double displace_y_parsed = Inkscape::Util::Quantity::convert( - displace_y * global_randomize, unit.get_abbreviation(), svg_units->abbr); + double displace_x_parsed = displace_x * global_randomize; + double displace_y_parsed = displace_y * global_randomize; Geom::Point output = Geom::Point(sign(displace_x_parsed), sign(displace_y_parsed)); return output; @@ -175,7 +161,6 @@ void LPERoughen::doEffect(SPCurve *curve) Geom::PathVector const original_pathv = pathv_to_linear_and_cubic_beziers(curve->get_pathvector()); curve->reset(); - Inkscape::Util::Unit const *svg_units = SP_ACTIVE_DESKTOP->namedview->svg_units; for (Geom::PathVector::const_iterator path_it = original_pathv.begin(); path_it != original_pathv.end(); ++path_it) { if (path_it->empty()) @@ -220,8 +205,7 @@ void LPERoughen::doEffect(SPCurve *curve) } else { nCurve->lineto(point3); } - double length = Inkscape::Util::Quantity::convert( - curve_it1->length(0.001), svg_units->abbr, unit.get_abbreviation()); + double length = curve_it1->length(0.001); std::size_t splits = 0; if (method == DM_SEGMENTS) { splits = segments; @@ -287,8 +271,8 @@ SPCurve *LPERoughen::addNodesAndJitter(const Geom::Curve *A, double t) } if (cubic) { std::pair<Geom::CubicBezier, Geom::CubicBezier> div = cubic->subdivide(t); - std::vector<Geom::Point> seg1 = div.first.points(), - seg2 = div.second.points(); + std::vector<Geom::Point> seg1 = div.first.controlPoints(), + seg2 = div.second.controlPoints(); out->moveto(seg1[0]); out->curveto(seg1[1] + point1, seg1[2] + point2, seg1[3] + point3); out->curveto(seg2[1] + point_b1, seg2[2], seg2[3]); diff --git a/src/live_effects/lpe-roughen.h b/src/live_effects/lpe-roughen.h index ed9f06cf7..2b285cd40 100644 --- a/src/live_effects/lpe-roughen.h +++ b/src/live_effects/lpe-roughen.h @@ -17,7 +17,6 @@ #include "live_effects/parameter/parameter.h" #include "live_effects/parameter/path.h" #include "live_effects/parameter/bool.h" -#include "live_effects/parameter/unit.h" #include "live_effects/parameter/random.h" namespace Inkscape { @@ -45,7 +44,6 @@ public: virtual Gtk::Widget *newWidget(); private: - UnitParam unit; EnumParam<DivisionMethod> method; ScalarParam max_segment_size; ScalarParam segments; diff --git a/src/live_effects/lpe-ruler.cpp b/src/live_effects/lpe-ruler.cpp index fd611c78d..49b5faa2e 100644 --- a/src/live_effects/lpe-ruler.cpp +++ b/src/live_effects/lpe-ruler.cpp @@ -111,7 +111,7 @@ LPERuler::ruler_mark(Geom::Point const &A, Geom::Point const &n, MarkType const break; } - Piecewise<D2<SBasis> > seg(D2<SBasis>(Linear(C[X], D[X]), Linear(C[Y], D[Y]))); + Piecewise<D2<SBasis> > seg(D2<SBasis>(SBasis(C[X], D[X]), SBasis(C[Y], D[Y]))); return seg; } diff --git a/src/live_effects/lpe-show_handles.cpp b/src/live_effects/lpe-show_handles.cpp index 2638f312e..0bc1c4f17 100644 --- a/src/live_effects/lpe-show_handles.cpp +++ b/src/live_effects/lpe-show_handles.cpp @@ -79,9 +79,9 @@ void LPEShowHandles::doBeforeEffect (SPLPEItem const* lpeitem) stroke_width = item->style->stroke_width.computed; } -std::vector<Geom::Path> LPEShowHandles::doEffect_path (std::vector<Geom::Path> const & path_in) +Geom::PathVector LPEShowHandles::doEffect_path (Geom::PathVector const & path_in) { - std::vector<Geom::Path> path_out; + Geom::PathVector path_out; Geom::PathVector const original_pathv = pathv_to_linear_and_cubic_beziers(path_in); if(original_path) { for (unsigned int i=0; i < path_in.size(); i++) { @@ -112,14 +112,14 @@ LPEShowHandles::generateHelperPath(Geom::PathVector result) continue; } //Itreadores - 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::iterator curve_it1 = path_it->begin(); // incoming curve + Geom::Path::iterator curve_it2 = ++(path_it->begin()); // outgoing curve + Geom::Path::iterator curve_endit = path_it->end_default(); // this determines when the loop has to stop 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::Curve const &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 @@ -165,9 +165,7 @@ LPEShowHandles::drawNode(Geom::Point p) char const * svgd; svgd = "M 0.05,0 A 0.05,0.05 0 0 1 0,0.05 0.05,0.05 0 0 1 -0.05,0 0.05,0.05 0 0 1 0,-0.05 0.05,0.05 0 0 1 0.05,0 Z M -0.5,-0.5 0.5,-0.5 0.5,0.5 -0.5,0.5 Z"; Geom::PathVector pathv = sp_svg_read_pathv(svgd); - pathv *= Geom::Rotate::from_degrees(rotate_nodes); - pathv *= Geom::Scale (diameter); - pathv += p; + pathv *= Geom::Rotate::from_degrees(rotate_nodes) * Geom::Scale(diameter) * Geom::Translate(p); outline_path.push_back(pathv[0]); outline_path.push_back(pathv[1]); } @@ -181,8 +179,7 @@ LPEShowHandles::drawHandle(Geom::Point p) char const * svgd; svgd = "M 0.7,0.35 A 0.35,0.35 0 0 1 0.35,0.7 0.35,0.35 0 0 1 0,0.35 0.35,0.35 0 0 1 0.35,0 0.35,0.35 0 0 1 0.7,0.35 Z"; Geom::PathVector pathv = sp_svg_read_pathv(svgd); - pathv *= Geom::Scale (diameter); - pathv += p-Geom::Point(diameter * 0.35,diameter * 0.35); + pathv *= Geom::Scale (diameter) * Geom::Translate(p - Geom::Point(diameter * 0.35,diameter * 0.35)); outline_path.push_back(pathv[0]); } } diff --git a/src/live_effects/lpe-show_handles.h b/src/live_effects/lpe-show_handles.h index 77b28e77a..34390dd32 100644 --- a/src/live_effects/lpe-show_handles.h +++ b/src/live_effects/lpe-show_handles.h @@ -36,7 +36,7 @@ public: protected: - virtual std::vector<Geom::Path> doEffect_path (std::vector<Geom::Path> const & path_in); + virtual Geom::PathVector doEffect_path (Geom::PathVector const & path_in); private: diff --git a/src/live_effects/lpe-simplify.cpp b/src/live_effects/lpe-simplify.cpp index 7fc20ede1..265192a17 100644 --- a/src/live_effects/lpe-simplify.cpp +++ b/src/live_effects/lpe-simplify.cpp @@ -164,14 +164,13 @@ LPESimplify::generateHelperPathAndSmooth(Geom::PathVector &result) Geom::PathVector tmp_path; Geom::CubicBezier const *cubic = NULL; for (Geom::PathVector::iterator path_it = result.begin(); path_it != result.end(); ++path_it) { - //Si está vacío... if (path_it->empty()) { continue; } - //Itreadores - 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::iterator curve_it1 = path_it->begin(); // incoming curve + Geom::Path::iterator curve_it2 = ++(path_it->begin());// outgoing curve + Geom::Path::iterator curve_endit = path_it->end_default(); // this determines when the loop has to stop SPCurve *nCurve = new SPCurve(); if (path_it->closed()) { // if the path is closed, maybe we have to stop a bit earlier because the @@ -264,8 +263,7 @@ LPESimplify::drawNode(Geom::Point p) char const * svgd; svgd = "M 0.55,0.5 A 0.05,0.05 0 0 1 0.5,0.55 0.05,0.05 0 0 1 0.45,0.5 0.05,0.05 0 0 1 0.5,0.45 0.05,0.05 0 0 1 0.55,0.5 Z M 0,0 1,0 1,1 0,1 Z"; Geom::PathVector pathv = sp_svg_read_pathv(svgd); - pathv *= Geom::Affine(r,0,0,r,0,0); - pathv += p - Geom::Point(0.5*r,0.5*r); + pathv *= Geom::Scale(r) * Geom::Translate(p - Geom::Point(0.5*r,0.5*r)); hp.push_back(pathv[0]); hp.push_back(pathv[1]); } @@ -277,8 +275,7 @@ LPESimplify::drawHandle(Geom::Point p) char const * svgd; svgd = "M 0.7,0.35 A 0.35,0.35 0 0 1 0.35,0.7 0.35,0.35 0 0 1 0,0.35 0.35,0.35 0 0 1 0.35,0 0.35,0.35 0 0 1 0.7,0.35 Z"; Geom::PathVector pathv = sp_svg_read_pathv(svgd); - pathv *= Geom::Affine(r,0,0,r,0,0); - pathv += p - Geom::Point(0.35*r,0.35*r); + pathv *= Geom::Scale(r) * Geom::Translate(p - Geom::Point(0.35*r,0.35*r)); hp.push_back(pathv[0]); } diff --git a/src/live_effects/lpe-skeleton.cpp b/src/live_effects/lpe-skeleton.cpp index c61e9773f..6e4afbe9b 100644 --- a/src/live_effects/lpe-skeleton.cpp +++ b/src/live_effects/lpe-skeleton.cpp @@ -62,10 +62,10 @@ LPESkeleton::doEffect (SPCurve * curve) // spice this up to make the effect actually *do* something! } -std::vector<Geom::Path> -LPESkeleton::doEffect_path (std::vector<Geom::Path> const & path_in) +Geom::PathVector +LPESkeleton::doEffect_path (Geom::PathVector const & path_in) { - std::vector<Geom::Path> path_out; + Geom::PathVector path_out; path_out = path_in; // spice this up to make the effect actually *do* something! diff --git a/src/live_effects/lpe-skeleton.h b/src/live_effects/lpe-skeleton.h index 124d1a4cb..3b45b6978 100644 --- a/src/live_effects/lpe-skeleton.h +++ b/src/live_effects/lpe-skeleton.h @@ -36,8 +36,8 @@ public: // Choose to implement one of the doEffect functions. You can delete or comment out the others. // virtual void doEffect (SPCurve * curve); -// virtual std::vector<Geom::Path> doEffect_path (std::vector<Geom::Path> const & path_in); - virtual Geom::Piecewise<Geom::D2<Geom::SBasis> > doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in); +// virtual Geom::PathVector doEffect_path (Geom::PathVector const &path_in); + virtual Geom::Piecewise<Geom::D2<Geom::SBasis> > doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const &pwd2_in); /* the knotholder entity classes (if any) can be declared friends */ //friend class Skeleton::KnotHolderEntityMyHandle; diff --git a/src/live_effects/lpe-sketch.cpp b/src/live_effects/lpe-sketch.cpp index 551dbe16a..82d343f6e 100644 --- a/src/live_effects/lpe-sketch.cpp +++ b/src/live_effects/lpe-sketch.cpp @@ -346,7 +346,7 @@ LPESketch::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_ //TODO: put this 4 as a parameter in the UI... //TODO: what if with v=0? double l = tgtlength*(1-tgtlength_rdm)/v_t.length(); - double r = std::pow(v_t.length(),3)/cross(a_t,v_t); + double r = std::pow(v_t.length(), 3) / cross(v_t, a_t); r = sqrt((2*fabs(r)-tgtscale)*tgtscale)/v_t.length(); l=(r<l)?r:l; //collect the tgt segment into output. diff --git a/src/live_effects/lpe-spiro.cpp b/src/live_effects/lpe-spiro.cpp index 8b4274ab2..eefd25c7d 100644 --- a/src/live_effects/lpe-spiro.cpp +++ b/src/live_effects/lpe-spiro.cpp @@ -10,8 +10,7 @@ #include <typeinfo> #include <2geom/pathvector.h> #include <2geom/affine.h> -#include <2geom/bezier-curve.h> -#include <2geom/hvlinesegment.h> +#include <2geom/curves.h> #include "helper/geom-nodetype.h" #include "helper/geom-curves.h" diff --git a/src/live_effects/lpe-tangent_to_curve.cpp b/src/live_effects/lpe-tangent_to_curve.cpp index bce4876af..978ab57fb 100644 --- a/src/live_effects/lpe-tangent_to_curve.cpp +++ b/src/live_effects/lpe-tangent_to_curve.cpp @@ -90,7 +90,7 @@ LPETangentToCurve::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const C = ptA - derivA * length_left; D = ptA + derivA * length_right; - output = Piecewise<D2<SBasis> >(D2<SBasis>(Linear(C[X], D[X]), Linear(C[Y], D[Y]))); + output = Piecewise<D2<SBasis> >(D2<SBasis>(SBasis(C[X], D[X]), SBasis(C[Y], D[Y]))); return output; } @@ -135,7 +135,7 @@ KnotHolderEntityAttachPt::knot_set(Geom::Point const &p, Geom::Point const &/*or } Piecewise<D2<SBasis> > pwd2 = paths_to_pw( lpe->pathvector_before_effect ); - double t0 = nearest_point(s, pwd2); + double t0 = nearest_time(s, pwd2); lpe->t_attach.param_set_value(t0); // FIXME: this should not directly ask for updating the item. It should write to SVG, which triggers updating. @@ -149,7 +149,7 @@ KnotHolderEntityLeftEnd::knot_set(Geom::Point const &p, Geom::Point const &/*ori Geom::Point const s = snap_knot_position(p, state); - double lambda = Geom::nearest_point(s, lpe->ptA, lpe->derivA); + double lambda = Geom::nearest_time(s, lpe->ptA, lpe->derivA); lpe->length_left.param_set_value(-lambda); sp_lpe_item_update_patheffect (SP_LPE_ITEM(item), false, true); @@ -162,7 +162,7 @@ KnotHolderEntityRightEnd::knot_set(Geom::Point const &p, Geom::Point const &/*or Geom::Point const s = snap_knot_position(p, state); - double lambda = Geom::nearest_point(s, lpe->ptA, lpe->derivA); + double lambda = Geom::nearest_time(s, lpe->ptA, lpe->derivA); lpe->length_right.param_set_value(lambda); sp_lpe_item_update_patheffect (SP_LPE_ITEM(item), false, true); diff --git a/src/live_effects/lpe-taperstroke.cpp b/src/live_effects/lpe-taperstroke.cpp index 2c74af6d6..ef616f802 100644 --- a/src/live_effects/lpe-taperstroke.cpp +++ b/src/live_effects/lpe-taperstroke.cpp @@ -14,7 +14,6 @@ #include "live_effects/lpe-taperstroke.h" #include <2geom/path.h> -#include <2geom/shape.h> #include <2geom/path.h> #include <2geom/circle.h> #include <2geom/sbasis-to-bezier.h> @@ -126,6 +125,7 @@ void LPETaperStroke::doOnApply(SPLPEItem const* lpeitem) sp_repr_css_attr_unref (css); line_width.param_set_value(width); + line_width.write_to_SVG(); } else { printf("WARNING: It only makes sense to apply Taper stroke to paths (not groups).\n"); } @@ -193,7 +193,7 @@ Piecewise<D2<SBasis> > stretch_along(Piecewise<D2<SBasis> > pwd2_in, Geom::Path Geom::PathVector LPETaperStroke::doEffect_path(Geom::PathVector const& path_in) { Geom::Path first_cusp = return_at_first_cusp(path_in[0]); - Geom::Path last_cusp = return_at_first_cusp(path_in[0].reverse()); + Geom::Path last_cusp = return_at_first_cusp(path_in[0].reversed()); bool zeroStart = false; // [distance from start taper knot -> start of path] == 0 bool zeroEnd = false; // [distance from end taper knot -> end of path] == 0 @@ -275,7 +275,7 @@ Geom::PathVector LPETaperStroke::doEffect_path(Geom::PathVector const& path_in) pat_str << "M 1,0 C " << 1 - (double)smoothing << ",0 0,0.5 0,0.5 0,0.5 " << 1 - (double)smoothing << ",1 1,1"; pat_vec = sp_svg_read_pathv(pat_str.str().c_str()); - pwd2.concat(stretch_along(pathv_out[0].toPwSb(), pat_vec[0], -fabs(line_width))); + pwd2.concat(stretch_along(pathv_out[0].toPwSb(), pat_vec[0], fabs(line_width))); throwaway_path = Geom::path_from_piecewise(pwd2, LPE_CONVERSION_TOLERANCE)[0]; real_path.append(throwaway_path); @@ -304,7 +304,7 @@ Geom::PathVector LPETaperStroke::doEffect_path(Geom::PathVector const& path_in) pat_vec = sp_svg_read_pathv(pat_str_1.str().c_str()); pwd2 = Piecewise<D2<SBasis> >(); - pwd2.concat(stretch_along(pathv_out[2].toPwSb(), pat_vec[0], -fabs(line_width))); + pwd2.concat(stretch_along(pathv_out[2].toPwSb(), pat_vec[0], fabs(line_width))); throwaway_path = Geom::path_from_piecewise(pwd2, LPE_CONVERSION_TOLERANCE)[0]; if (!Geom::are_near(real_path.finalPoint(), throwaway_path.initialPoint()) && real_path.size() >= 1) { @@ -317,7 +317,7 @@ Geom::PathVector LPETaperStroke::doEffect_path(Geom::PathVector const& path_in) if (!metInMiddle) { // append the inside outline of the path (against direction) - throwaway_path = half_outline(pathv_out[1].reverse(), fabs(line_width)/2., miter_limit, static_cast<LineJoinType>(join_type.get_value())); + throwaway_path = half_outline(pathv_out[1].reversed(), fabs(line_width)/2., miter_limit, static_cast<LineJoinType>(join_type.get_value())); if (!Geom::are_near(real_path.finalPoint(), throwaway_path.initialPoint()) && real_path.size() >= 1) { real_path.appendNew<Geom::LineSegment>(throwaway_path.initialPoint()); @@ -389,13 +389,9 @@ Piecewise<D2<SBasis> > stretch_along(Piecewise<D2<SBasis> > pwd2_in, Geom::Path x0 -= pattBndsX->min(); y0 -= pattBndsY->middle(); - double xspace = 0; double noffset = 0; double toffset = 0; // Prevent more than 90% overlap... - if (xspace < -pattBndsX->extent()*.9) { - xspace = -pattBndsX->extent()*.9; - } y0+=noffset; @@ -486,7 +482,7 @@ void KnotHolderEntityAttachBegin::knot_set(Geom::Point const &p, Geom::Point con Geom::Path p_in = return_at_first_cusp(pathv[0]); pwd2.concat(p_in.toPwSb()); - double t0 = nearest_point(s, pwd2); + double t0 = nearest_time(s, pwd2); lpe->attach_start.param_set_value(t0); // FIXME: this should not directly ask for updating the item. It should write to SVG, which triggers updating. @@ -511,10 +507,10 @@ void KnotHolderEntityAttachEnd::knot_set(Geom::Point const &p, Geom::Point const return; } Geom::PathVector pathv = lpe->pathvector_before_effect; - Geom::Path p_in = return_at_first_cusp(pathv[0].reverse()); + Geom::Path p_in = return_at_first_cusp(pathv[0].reversed()); Piecewise<D2<SBasis> > pwd2 = p_in.toPwSb(); - double t0 = nearest_point(s, pwd2); + double t0 = nearest_time(s, pwd2); lpe->attach_end.param_set_value(t0); sp_lpe_item_update_patheffect (SP_LPE_ITEM(item), false, true); diff --git a/src/live_effects/lpe-test-doEffect-stack.cpp b/src/live_effects/lpe-test-doEffect-stack.cpp index c6787aae1..2bcd4c136 100644 --- a/src/live_effects/lpe-test-doEffect-stack.cpp +++ b/src/live_effects/lpe-test-doEffect-stack.cpp @@ -47,14 +47,14 @@ LPEdoEffectStackTest::doEffect (SPCurve * curve) } } -std::vector<Geom::Path> -LPEdoEffectStackTest::doEffect_path (std::vector<Geom::Path> const & path_in) +Geom::PathVector +LPEdoEffectStackTest::doEffect_path (Geom::PathVector const &path_in) { if (step >= 2) { return Effect::doEffect_path(path_in); } else { // return here - std::vector<Geom::Path> path_out = path_in; + Geom::PathVector path_out = path_in; return path_out; } } diff --git a/src/live_effects/lpe-test-doEffect-stack.h b/src/live_effects/lpe-test-doEffect-stack.h index fa3ee09be..208a345c6 100644 --- a/src/live_effects/lpe-test-doEffect-stack.h +++ b/src/live_effects/lpe-test-doEffect-stack.h @@ -27,7 +27,7 @@ public: virtual ~LPEdoEffectStackTest(); virtual void doEffect (SPCurve * curve); - virtual std::vector<Geom::Path> doEffect_path (std::vector<Geom::Path> const & path_in); + virtual Geom::PathVector doEffect_path (Geom::PathVector const & path_in); virtual Geom::Piecewise<Geom::D2<Geom::SBasis> > doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in); private: diff --git a/src/live_effects/lpe-vonkoch.cpp b/src/live_effects/lpe-vonkoch.cpp index b52816c87..7b424e019 100644 --- a/src/live_effects/lpe-vonkoch.cpp +++ b/src/live_effects/lpe-vonkoch.cpp @@ -76,12 +76,12 @@ LPEVonKoch::~LPEVonKoch() } -std::vector<Geom::Path> -LPEVonKoch::doEffect_path (std::vector<Geom::Path> const & path_in) +Geom::PathVector +LPEVonKoch::doEffect_path (Geom::PathVector const & path_in) { using namespace Geom; - std::vector<Geom::Path> generating_path = generator.get_pathvector(); + Geom::PathVector generating_path = generator.get_pathvector(); if (generating_path.empty()) { return path_in; @@ -148,15 +148,15 @@ LPEVonKoch::doEffect_path (std::vector<Geom::Path> const & path_in) } //Generate path: - std::vector<Geom::Path> pathi = path_in; - std::vector<Geom::Path> path_out = path_in; + Geom::PathVector pathi = path_in; + Geom::PathVector path_out = path_in; for (unsigned i = 0; i<nbgenerations; i++){ if (drawall.get_value()){ path_out = path_in; complexity = path_in_complexity; }else{ - path_out = std::vector<Geom::Path>(); + path_out = Geom::PathVector(); complexity = 0; } for (unsigned j = 0; j<transforms.size(); j++){ @@ -203,7 +203,7 @@ LPEVonKoch::doEffect_path (std::vector<Geom::Path> const & path_in) hp_vec.push_back(refbox_as_vect); } //Draw the transformed boxes - std::vector<Geom::Path> generating_path = generator.get_pathvector(); + Geom::PathVector generating_path = generator.get_pathvector(); for (unsigned i=0;i<generating_path.size(); i++){ if (generating_path[i].size()==0){ //Ooops! this should not happen. @@ -243,7 +243,7 @@ LPEVonKoch::doBeforeEffect (SPLPEItem const* lpeitem) using namespace Geom; original_bbox(lpeitem); - std::vector<Geom::Path> paths = ref_path.get_pathvector(); + Geom::PathVector paths = ref_path.get_pathvector(); Geom::Point A,B; if (paths.empty()||paths.front().size()==0){ //FIXME: a path is used as ref instead of 2 points to work around path/point param incompatibility bug. @@ -258,7 +258,7 @@ LPEVonKoch::doBeforeEffect (SPLPEItem const* lpeitem) if (paths.size()!=1||paths.front().size()!=1){ Geom::Path tmp_path(A); tmp_path.appendNew<LineSegment>(B); - std::vector<Geom::Path> tmp_pathv; + Geom::PathVector tmp_pathv; tmp_pathv.push_back(tmp_path); ref_path.set_new_value(tmp_pathv,true); } @@ -282,7 +282,7 @@ LPEVonKoch::resetDefaults(SPItem const* item) B[Geom::X] = boundingbox_X.max(); B[Geom::Y] = boundingbox_Y.middle(); - std::vector<Geom::Path> paths,refpaths; + Geom::PathVector paths,refpaths; Geom::Path path = Geom::Path(A); path.appendNew<Geom::LineSegment>(B); @@ -298,7 +298,7 @@ LPEVonKoch::resetDefaults(SPItem const* item) //refA[Geom::Y] = boundingbox_Y.middle(); //refB[Geom::X] = boundingbox_X.max(); //refB[Geom::Y] = boundingbox_Y.middle(); - //std::vector<Geom::Path> paths; + //Geom::PathVector paths; //Geom::Path path = Geom::Path( (Point) refA); //path.appendNew<Geom::LineSegment>( (Point) refB ); //paths.push_back(path * Affine(1./3,0,0,1./3, refA[X]*2./3, refA[Y]*2./3 + boundingbox_Y.extent()/2)); diff --git a/src/live_effects/lpe-vonkoch.h b/src/live_effects/lpe-vonkoch.h index 7dff2be52..bffbebd54 100644 --- a/src/live_effects/lpe-vonkoch.h +++ b/src/live_effects/lpe-vonkoch.h @@ -49,7 +49,7 @@ public: LPEVonKoch(LivePathEffectObject *lpeobject); virtual ~LPEVonKoch(); - virtual std::vector<Geom::Path> doEffect_path (std::vector<Geom::Path> const & path_in); + virtual Geom::PathVector doEffect_path (Geom::PathVector const & path_in); virtual void resetDefaults(SPItem const* item); diff --git a/src/live_effects/lpeobject-reference.cpp b/src/live_effects/lpeobject-reference.cpp index 573c8a2fd..d9de6e77f 100644 --- a/src/live_effects/lpeobject-reference.cpp +++ b/src/live_effects/lpeobject-reference.cpp @@ -43,14 +43,7 @@ LPEObjectReference::~LPEObjectReference(void) bool LPEObjectReference::_acceptObject(SPObject * const obj) const { if (IS_LIVEPATHEFFECT(obj)) { - SPObject * const owner = getOwner(); - /* Refuse references to us or to an ancestor. */ - for ( SPObject *iter = owner ; iter ; iter = iter->parent ) { - if ( iter == obj ) { - return false; - } - } - return true; + return URIReference::_acceptObject(obj); } else { return false; } diff --git a/src/live_effects/parameter/filletchamferpointarray.cpp b/src/live_effects/parameter/filletchamferpointarray.cpp index e9d375b93..b089213fd 100644 --- a/src/live_effects/parameter/filletchamferpointarray.cpp +++ b/src/live_effects/parameter/filletchamferpointarray.cpp @@ -11,7 +11,6 @@ #include <2geom/piecewise.h> #include <2geom/sbasis-to-bezier.h> #include <2geom/sbasis-geometric.h> -#include <2geom/svg-elliptical-arc.h> #include <2geom/line.h> #include <2geom/path-intersection.h> @@ -205,7 +204,7 @@ void FilletChamferPointArrayParam::recalculate_controlpoints_for_new_pwd2( //delete temp vector std::vector<Point>().swap(tmp); if (last_pathv.size() > counterPaths) { - last_pathv[counterPaths] = last_pathv[counterPaths].reverse(); + last_pathv[counterPaths] = last_pathv[counterPaths].reversed(); } } else { if (last_pathv.size() > counterPaths) { @@ -223,7 +222,7 @@ void FilletChamferPointArrayParam::recalculate_controlpoints_for_new_pwd2( } double xPos = 0; if (_vector[1][X] > 0) { - xPos = nearest_point(curve_it1->initialPoint(), pwd2_in); + xPos = nearest_time(curve_it1->initialPoint(), pwd2_in); } if (nodetype == NODE_CUSP) { result.push_back(Point(xPos, 1)); @@ -234,7 +233,7 @@ void FilletChamferPointArrayParam::recalculate_controlpoints_for_new_pwd2( double xPos = _vector[counter - offset][X]; if (_vector.size() <= (unsigned)(counter - offset)) { if (_vector[1][X] > 0) { - xPos = nearest_point(curve_it1->initialPoint(), pwd2_in); + xPos = nearest_time(curve_it1->initialPoint(), pwd2_in); } else { xPos = 0; } @@ -270,6 +269,9 @@ void FilletChamferPointArrayParam::recalculate_knots( Piecewise<D2<SBasis> > const &pwd2_in) { bool change = false; + if(_vector.size() == 0){ + return; + } PathVector pathv = path_from_piecewise(pwd2_in, 0.001); if (!pathv.empty()) { std::vector<Point> result; @@ -354,11 +356,6 @@ void FilletChamferPointArrayParam::set_pwd2( last_pwd2_normal = pwd2_normal_in; } -void FilletChamferPointArrayParam::set_document_unit(Glib::ustring value_document_unit) -{ - documentUnit = value_document_unit; -} - void FilletChamferPointArrayParam::set_helper_size(int hs) { helper_size = hs; @@ -374,11 +371,6 @@ void FilletChamferPointArrayParam::set_use_distance(bool use_knot_distance ) use_distance = use_knot_distance; } -void FilletChamferPointArrayParam::set_unit(const gchar *abbr) -{ - unit = abbr; -} - void FilletChamferPointArrayParam::updateCanvasIndicators() { std::vector<Point> ts = data(); @@ -403,8 +395,8 @@ void FilletChamferPointArrayParam::updateCanvasIndicators() Geom::Affine aff = Geom::Affine(); aff *= Geom::Scale(helper_size); aff *= Geom::Rotate(ray1.angle() - deg_to_rad(270)); + aff *= Geom::Translate(last_pwd2[i].valueAt(Xvalue)); pathv *= aff; - pathv += last_pwd2[i].valueAt(Xvalue); hp.push_back(pathv[0]); hp.push_back(pathv[1]); i++; @@ -420,7 +412,7 @@ void FilletChamferPointArrayParam::addCanvasIndicators( double FilletChamferPointArrayParam::rad_to_len(int index, double rad) { double len = 0; - std::vector<Geom::Path> subpaths = path_from_piecewise(last_pwd2, 0.1); + Geom::PathVector subpaths = path_from_piecewise(last_pwd2, 0.1); std::pair<std::size_t, std::size_t> positions = get_positions(index, subpaths); D2<SBasis> A = last_pwd2[last_index(index, subpaths)]; if(positions.second != 0){ @@ -438,7 +430,7 @@ double FilletChamferPointArrayParam::rad_to_len(int index, double rad) Geom::Crossings cs = Geom::crossings(p0, p1); if(cs.size() > 0){ Point cp =p0(cs[0].ta); - double p0pt = nearest_point(cp, B); + double p0pt = nearest_time(cp, B); len = time_to_len(index,p0pt); } else { if(rad < 0){ @@ -453,7 +445,7 @@ double FilletChamferPointArrayParam::len_to_rad(int index, double len) double rad = 0; double tmp_len = _vector[index][X]; _vector[index] = Geom::Point(len,_vector[index][Y]); - std::vector<Geom::Path> subpaths = path_from_piecewise(last_pwd2, 0.1); + Geom::PathVector subpaths = path_from_piecewise(last_pwd2, 0.1); std::pair<std::size_t, std::size_t> positions = get_positions(index, subpaths); Piecewise<D2<SBasis> > u; u.push_cut(0); @@ -485,14 +477,14 @@ double FilletChamferPointArrayParam::len_to_rad(int index, double len) if (cubic2) { ray2.setPoints(endArcPoint, (*cubic2)[1]); } - bool ccwToggle = cross(A->finalPoint() - startArcPoint, endArcPoint - startArcPoint) < 0; + bool ccwToggle = cross(A->finalPoint() - startArcPoint, endArcPoint - startArcPoint) > 0; double distanceArc = Geom::distance(startArcPoint,middle_point(startArcPoint,endArcPoint)); double angleBetween = angle_between(ray1, ray2, ccwToggle); rad = distanceArc/sin(angleBetween/2.0); return rad * -1; } -std::vector<double> FilletChamferPointArrayParam::get_times(int index, std::vector<Geom::Path> subpaths, bool last) +std::vector<double> FilletChamferPointArrayParam::get_times(int index, Geom::PathVector subpaths, bool last) { const double tolerance = 0.001; const double gapHelper = 0.00001; @@ -501,12 +493,18 @@ std::vector<double> FilletChamferPointArrayParam::get_times(int index, std::vect curve_it1 = subpaths[positions.first][positions.second].duplicate(); Coord it1_length = (*curve_it1).length(tolerance); double time_it1, time_it2, time_it1_B, intpart; + if(_vector.size() <= index){ + std::vector<double> out; + out.push_back(0); + out.push_back(1); + out.push_back(0); + return out; + } time_it1 = modf(to_time(index, _vector[index][X]), &intpart); if (_vector[index][Y] == 0) { time_it1 = 0; } double resultLenght = 0; - time_it1_B = 1; if (subpaths[positions.first].closed() && last) { time_it2 = modf(to_time(index - positions.second , _vector[index - positions.second ][X]), &intpart); resultLenght = it1_length + to_len(index - positions.second, _vector[index - positions.second ][X]); @@ -541,7 +539,7 @@ std::vector<double> FilletChamferPointArrayParam::get_times(int index, std::vect return out; } -std::pair<std::size_t, std::size_t> FilletChamferPointArrayParam::get_positions(int index, std::vector<Geom::Path> subpaths) +std::pair<std::size_t, std::size_t> FilletChamferPointArrayParam::get_positions(int index, Geom::PathVector subpaths) { int counter = -1; std::size_t first = 0; @@ -583,7 +581,7 @@ std::pair<std::size_t, std::size_t> FilletChamferPointArrayParam::get_positions( return out; } -int FilletChamferPointArrayParam::last_index(int index, std::vector<Geom::Path> subpaths) +int FilletChamferPointArrayParam::last_index(int index, Geom::PathVector subpaths) { int counter = -1; bool inSubpath = false; @@ -709,9 +707,9 @@ void FilletChamferPointArrayParamKnotHolderEntity::knot_set(Point const &p, return; } Piecewise<D2<SBasis> > const &pwd2 = _pparam->get_pwd2(); - double t = nearest_point(p, pwd2[_index]); + double t = nearest_time(p, pwd2[_index]); Geom::Point const s = snap_knot_position(pwd2[_index].valueAt(t), state); - t = nearest_point(s, pwd2[_index]); + t = nearest_time(s, pwd2[_index]); if (t == 1) { t = 0.9999; } @@ -803,7 +801,7 @@ void FilletChamferPointArrayParamKnotHolderEntity::knot_click(guint state) if(xModified < 0 && !_pparam->use_distance){ xModified = _pparam->len_to_rad(_index, _pparam->_vector.at(_index).x()); } - std::vector<Geom::Path> subpaths = path_from_piecewise(_pparam->last_pwd2, 0.1); + Geom::PathVector subpaths = path_from_piecewise(_pparam->last_pwd2, 0.1); std::pair<std::size_t, std::size_t> positions = _pparam->get_positions(_index, subpaths); D2<SBasis> A = _pparam->last_pwd2[_pparam->last_index(_index, subpaths)]; if(positions.second != 0){ @@ -813,7 +811,7 @@ void FilletChamferPointArrayParamKnotHolderEntity::knot_click(guint state) bool aprox = (A[0].degreesOfFreedom() != 2 || B[0].degreesOfFreedom() != 2) && !_pparam->use_distance?true:false; Geom::Point offset = Geom::Point(xModified, _pparam->_vector.at(_index).y()); Inkscape::UI::Dialogs::FilletChamferPropertiesDialog::showDialog( - this->desktop, offset, this, _pparam->unit, _pparam->use_distance, aprox, _pparam->documentUnit); + this->desktop, offset, this, _pparam->use_distance, aprox); } } diff --git a/src/live_effects/parameter/filletchamferpointarray.h b/src/live_effects/parameter/filletchamferpointarray.h index 7849d5afb..48cd26d2d 100644 --- a/src/live_effects/parameter/filletchamferpointarray.h +++ b/src/live_effects/parameter/filletchamferpointarray.h @@ -47,14 +47,12 @@ public: virtual double len_to_rad(int index, double len); virtual double len_to_time(int index, double len); virtual double time_to_len(int index, double time); - virtual std::pair<std::size_t, std::size_t> get_positions(int index, std::vector<Geom::Path> subpaths); - virtual int last_index(int index, std::vector<Geom::Path> subpaths); - std::vector<double> get_times(int index, std::vector<Geom::Path> subpaths, bool last); + virtual std::pair<std::size_t, std::size_t> get_positions(int index, Geom::PathVector subpaths); + virtual int last_index(int index, Geom::PathVector subpaths); + std::vector<double> get_times(int index, Geom::PathVector subpaths, bool last); virtual void set_helper_size(int hs); virtual void set_use_distance(bool use_knot_distance); virtual void set_chamfer_steps(int value_chamfer_steps); - virtual void set_document_unit(Glib::ustring value_document_unit); - virtual void set_unit(const gchar *abbr); virtual void addCanvasIndicators(SPLPEItem const *lpeitem, std::vector<Geom::PathVector> &hp_vec); virtual bool providesKnotHolderEntities() const { @@ -89,8 +87,6 @@ private: int helper_size; int chamfer_steps; bool use_distance; - const gchar *unit; - Glib::ustring documentUnit; Geom::PathVector hp; Geom::Piecewise<Geom::D2<Geom::SBasis> > last_pwd2; diff --git a/src/live_effects/parameter/originalpath.cpp b/src/live_effects/parameter/originalpath.cpp index 0884c4c9c..2741461be 100644 --- a/src/live_effects/parameter/originalpath.cpp +++ b/src/live_effects/parameter/originalpath.cpp @@ -8,10 +8,6 @@ # include "config.h" #endif -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include <gtkmm/box.h> #include "live_effects/parameter/originalpath.h" diff --git a/src/live_effects/parameter/originalpatharray.cpp b/src/live_effects/parameter/originalpatharray.cpp index 7706dbdf8..78e061e66 100644 --- a/src/live_effects/parameter/originalpatharray.cpp +++ b/src/live_effects/parameter/originalpatharray.cpp @@ -8,10 +8,6 @@ # include "config.h" #endif -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include "live_effects/parameter/originalpatharray.h" #include <gtkmm/widget.h> diff --git a/src/live_effects/parameter/originalpatharray.h b/src/live_effects/parameter/originalpatharray.h index 6c792613f..296c0f7f7 100644 --- a/src/live_effects/parameter/originalpatharray.h +++ b/src/live_effects/parameter/originalpatharray.h @@ -40,7 +40,7 @@ public: } gchar *href; URIReference ref; - std::vector<Geom::Path> _pathvector; + Geom::PathVector _pathvector; bool reversed; sigc::connection linked_changed_connection; diff --git a/src/live_effects/parameter/parameter.cpp b/src/live_effects/parameter/parameter.cpp index beb750404..527bc06fe 100644 --- a/src/live_effects/parameter/parameter.cpp +++ b/src/live_effects/parameter/parameter.cpp @@ -42,13 +42,6 @@ Parameter::param_write_to_repr(const char * svgd) param_effect->getRepr()->setAttribute(param_key.c_str(), svgd); } -// In gtk2, this wasn't an issue; we could toss around -// G_MAXDOUBLE and not worry about size allocations. But -// in gtk3, it is an issue: it allocates widget size for the maxmium -// value you pass to it, leading to some insane lengths. -// If you need this to be more, please be conservative about it. -const double SCALARPARAM_G_MAXDOUBLE = 10000000000.0; // TODO fixme: using an arbitrary large number as a magic value seems fragile. - void Parameter::write_to_SVG(void) { gchar * str = param_getSVGValue(); diff --git a/src/live_effects/parameter/parameter.h b/src/live_effects/parameter/parameter.h index 2e6cae49f..cc2c4f3d6 100644 --- a/src/live_effects/parameter/parameter.h +++ b/src/live_effects/parameter/parameter.h @@ -13,6 +13,13 @@ #include <2geom/forward.h> #include <2geom/pathvector.h> +// In gtk2, this wasn't an issue; we could toss around +// G_MAXDOUBLE and not worry about size allocations. But +// in gtk3, it is an issue: it allocates widget size for the maxmium +// value you pass to it, leading to some insane lengths. +// If you need this to be more, please be conservative about it. +const double SCALARPARAM_G_MAXDOUBLE = 10000000000.0; // TODO fixme: using an arbitrary large number as a magic value seems fragile. + class KnotHolder; class SPLPEItem; class SPDesktop; diff --git a/src/live_effects/parameter/path-reference.cpp b/src/live_effects/parameter/path-reference.cpp index a76fb1b32..42589b050 100644 --- a/src/live_effects/parameter/path-reference.cpp +++ b/src/live_effects/parameter/path-reference.cpp @@ -22,7 +22,7 @@ bool PathReference::_acceptObject(SPObject * const obj) const return false; } // TODO: check whether the referred path has this LPE applied, if so: deny deny deny! - return true; + return URIReference::_acceptObject(obj); } else { return false; } diff --git a/src/live_effects/parameter/path.cpp b/src/live_effects/parameter/path.cpp index ba95affd9..e0369e662 100644 --- a/src/live_effects/parameter/path.cpp +++ b/src/live_effects/parameter/path.cpp @@ -77,7 +77,7 @@ PathParam::~PathParam() g_free(defvalue); } -std::vector<Geom::Path> const & +Geom::PathVector const & PathParam::get_pathvector() const { return _pathvector; @@ -255,7 +255,7 @@ PathParam::param_transform_multiply(Geom::Affine const& postmul, bool /*set*/) } /* - * See comments for set_new_value(std::vector<Geom::Path>). + * See comments for set_new_value(Geom::PathVector). */ void PathParam::set_new_value (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & newpath, bool write_to_svg) @@ -291,7 +291,7 @@ PathParam::set_new_value (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & newpa * The new path data is not written to SVG. This method will emit the signal_path_changed signal. */ void -PathParam::set_new_value (std::vector<Geom::Path> const &newpath, bool write_to_svg) +PathParam::set_new_value (Geom::PathVector const &newpath, bool write_to_svg) { remove_link(); _pathvector = newpath; @@ -427,7 +427,13 @@ PathParam::paste_param_path(const char *svgd) if (svgd && *svgd) { // remove possible link to path remove_link(); - + SPItem * item = SP_ACTIVE_DESKTOP->getSelection()->singleItem(); + if (item != NULL) { + Geom::PathVector path_clipboard = sp_svg_read_pathv(svgd); + path_clipboard *= item->i2doc_affine().inverse(); + svgd = sp_svg_write_path( path_clipboard ); + } + param_write_to_repr(svgd); signal_path_pasted.emit(); } diff --git a/src/live_effects/parameter/path.h b/src/live_effects/parameter/path.h index 112a1ea13..d2dddbe97 100644 --- a/src/live_effects/parameter/path.h +++ b/src/live_effects/parameter/path.h @@ -31,7 +31,7 @@ public: const gchar * default_value = "M0,0 L1,1"); virtual ~PathParam(); - std::vector<Geom::Path> const & get_pathvector() const; + Geom::PathVector const & get_pathvector() const; Geom::Piecewise<Geom::D2<Geom::SBasis> > const & get_pwd2(); virtual Gtk::Widget * param_newWidget(); @@ -41,7 +41,7 @@ public: virtual void param_set_default(); void param_set_and_write_default(); - void set_new_value (std::vector<Geom::Path> const &newpath, bool write_to_svg); + void set_new_value (Geom::PathVector const &newpath, bool write_to_svg); void set_new_value (Geom::Piecewise<Geom::D2<Geom::SBasis> > const &newpath, bool write_to_svg); virtual void param_editOncanvas(SPItem * item, SPDesktop * dt); @@ -59,7 +59,7 @@ public: void on_paste_button_click(); protected: - std::vector<Geom::Path> _pathvector; // this is primary data storage, since it is closest to SVG. + Geom::PathVector _pathvector; // this is primary data storage, since it is closest to SVG. Geom::Piecewise<Geom::D2<Geom::SBasis> > _pwd2; // secondary, hence the bool must_recalculate_pwd2 bool must_recalculate_pwd2; // set when _pathvector was updated, but _pwd2 not diff --git a/src/live_effects/parameter/powerstrokepointarray.cpp b/src/live_effects/parameter/powerstrokepointarray.cpp index e0c2f4c68..c61e8f9cb 100644 --- a/src/live_effects/parameter/powerstrokepointarray.cpp +++ b/src/live_effects/parameter/powerstrokepointarray.cpp @@ -79,7 +79,7 @@ PowerStrokePointArrayParam::recalculate_controlpoints_for_new_pwd2(Geom::Piecewi Geom::Point pt = _vector[i]; Geom::Point position = last_pwd2.valueAt(pt[Geom::X]) + pt[Geom::Y] * last_pwd2_normal.valueAt(pt[Geom::X]); - double t = nearest_point(position, pwd2_in); + double t = nearest_time(position, pwd2_in); double offset = dot(position - pwd2_in.valueAt(t), normal.valueAt(t)); _vector[i] = Geom::Point(t, offset); } @@ -161,7 +161,7 @@ PowerStrokePointArrayParamKnotHolderEntity::knot_set(Geom::Point const &p, Geom: Piecewise<D2<SBasis> > const & n = _pparam->get_pwd2_normal(); Geom::Point const s = snap_knot_position(p, state); - double t = nearest_point(s, pwd2); + double t = nearest_time(s, pwd2); double offset = dot(s - pwd2.valueAt(t), n.valueAt(t)); _pparam->_vector.at(_index) = Geom::Point(t, offset); sp_lpe_item_update_patheffect(SP_LPE_ITEM(item), false, false); diff --git a/src/live_effects/spiro-converters.cpp b/src/live_effects/spiro-converters.cpp index 3c7bdf99e..f116d5256 100644 --- a/src/live_effects/spiro-converters.cpp +++ b/src/live_effects/spiro-converters.cpp @@ -64,7 +64,11 @@ ConverterSPCurve::curveto(double x1, double y1, double x2, double y2, double x3, }
-
+ConverterPath::ConverterPath(Geom::Path &path)
+ : _path(path)
+{
+ _path.setStitching(true);
+}
void
ConverterPath::moveto(double x, double y, bool is_open)
diff --git a/src/live_effects/spiro-converters.h b/src/live_effects/spiro-converters.h index 83f6ebbc3..90855d2d6 100644 --- a/src/live_effects/spiro-converters.h +++ b/src/live_effects/spiro-converters.h @@ -25,7 +25,7 @@ class ConverterSPCurve : public ConverterBase { public: ConverterSPCurve(SPCurve &curve) : _curve(curve) - {} ; + {} virtual void moveto(double x, double y, bool is_open); virtual void lineto(double x, double y); @@ -45,9 +45,7 @@ private: */ class ConverterPath : public ConverterBase { public: - ConverterPath(Geom::Path &path) - : _path(path) - {} ; + ConverterPath(Geom::Path &path); virtual void moveto(double x, double y, bool is_open); virtual void lineto(double x, double y); diff --git a/src/live_effects/spiro.h b/src/live_effects/spiro.h index 0d85da74b..066b44ca8 100644 --- a/src/live_effects/spiro.h +++ b/src/live_effects/spiro.h @@ -25,11 +25,9 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA #define INKSCAPE_SPIRO_H #include "live_effects/spiro-converters.h" +#include <2geom/forward.h> class SPCurve; -namespace Geom { - class Path; -} namespace Spiro { @@ -53,4 +51,4 @@ double get_knot_th(const spiro_seg *s, int i); } // namespace Spiro -#endif // INKSCAPE_SPIRO_H
\ No newline at end of file +#endif // INKSCAPE_SPIRO_H diff --git a/src/main.cpp b/src/main.cpp index 415118407..26c25af02 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1156,7 +1156,7 @@ static int sp_process_file_list(GSList *fl) } if (sp_export_svg) { if (sp_export_text_to_path) { - GSList *items = NULL; + std::vector<SPItem*> items; SPRoot *root = doc->getRoot(); doc->ensureUpToDate(); for ( SPObject *iter = root->firstChild(); iter ; iter = iter->getNext()) { @@ -1166,17 +1166,14 @@ static int sp_process_file_list(GSList *fl) } te_update_layout_now_recursive(item); - items = g_slist_append(items, item); + items.push_back(item); } - GSList *selected = NULL; - GSList *to_select = NULL; + std::vector<SPItem*> selected; + std::vector<Inkscape::XML::Node*> to_select; - sp_item_list_to_curves(items, &selected, &to_select); + sp_item_list_to_curves(items, selected, to_select); - g_slist_free (items); - g_slist_free (selected); - g_slist_free (to_select); } if(sp_export_id) { doc->ensureUpToDate(); @@ -1435,7 +1432,7 @@ static int sp_do_export_png(SPDocument *doc) g_warning ("--export-use-hints can only be used with --export-id or --export-area-drawing; ignored."); } - GSList *items = NULL; + std::vector<SPItem*> items; Geom::Rect area; if (sp_export_id || sp_export_area_drawing) { @@ -1459,7 +1456,7 @@ static int sp_do_export_png(SPDocument *doc) return 1; } - items = g_slist_prepend (items, SP_ITEM(o)); + items.push_back(SP_ITEM(o)); if (sp_export_id_only) { g_print("Exporting only object with id=\"%s\"; all other objects hidden\n", sp_export_id); @@ -1545,7 +1542,7 @@ static int sp_do_export_png(SPDocument *doc) } if (sp_export_area_snap) { - round_rectangle_outwards(area); + area = area.roundOutwards(); } // default dpi @@ -1644,10 +1641,12 @@ static int sp_do_export_png(SPDocument *doc) g_print("Background RRGGBBAA: %08x\n", bgcolor); g_print("Area %g:%g:%g:%g exported to %lu x %lu pixels (%g dpi)\n", area[Geom::X][0], area[Geom::Y][0], area[Geom::X][1], area[Geom::Y][1], width, height, dpi); - + + reverse(items.begin(),items.end()); + if ((width >= 1) && (height >= 1) && (width <= PNG_UINT_31_MAX) && (height <= PNG_UINT_31_MAX)) { if( sp_export_png_file(doc, path.c_str(), area, width, height, dpi, - dpi, bgcolor, NULL, NULL, true, sp_export_id_only ? items : NULL) == 1 ) { + dpi, bgcolor, NULL, NULL, true, sp_export_id_only ? items : std::vector<SPItem*>()) == 1 ) { g_print("Bitmap saved as: %s\n", filename.c_str()); } else { g_warning("Bitmap failed to save to: %s", filename.c_str()); @@ -1657,7 +1656,6 @@ static int sp_do_export_png(SPDocument *doc) } } - g_slist_free (items); return retcode; } diff --git a/src/object-snapper.cpp b/src/object-snapper.cpp index 338f91463..634d56aa6 100644 --- a/src/object-snapper.cpp +++ b/src/object-snapper.cpp @@ -19,6 +19,7 @@ #include <2geom/rect.h> #include <2geom/line.h> #include <2geom/circle.h> +#include <2geom/path-sink.h> #include "document.h" #include "sp-namedview.h" #include "sp-image.h" @@ -237,8 +238,9 @@ void Inkscape::ObjectSnapper::_collectNodes(SnapSourceType const &t, // current selection (see the comment in SelTrans::centerRequest()) bool old_pref2 = _snapmanager->snapprefs.isTargetSnappable(SNAPTARGET_ROTATION_CENTER); if (old_pref2) { - for ( GSList const *itemlist = _snapmanager->getRotationCenterSource(); itemlist != NULL; itemlist = g_slist_next(itemlist) ) { - if ((*i).item == reinterpret_cast<SPItem*>(itemlist->data)) { + std::vector<SPItem*> rotationSource=_snapmanager->getRotationCenterSource(); + for ( std::vector<SPItem*>::const_iterator itemlist = rotationSource.begin(); itemlist != rotationSource.end(); itemlist++) { + if ((*i).item == *itemlist) { // don't snap to this item's rotation center _snapmanager->snapprefs.setTargetSnappable(SNAPTARGET_ROTATION_CENTER, false); break; @@ -516,14 +518,14 @@ void Inkscape::ObjectSnapper::_snapPaths(IntermSnapResults &isr, for(Geom::PathVector::iterator it_pv = (it_p->path_vector)->begin(); it_pv != (it_p->path_vector)->end(); ++it_pv) { // Find a nearest point for each curve within this path // n curves will return n time values with 0 <= t <= 1 - std::vector<double> anp = (*it_pv).nearestPointPerCurve(p_doc); + std::vector<double> anp = (*it_pv).nearestTimePerCurve(p_doc); //std::cout << "#nearest points = " << anp.size() << " | p = " << p.getPoint() << std::endl; // Now we will examine each of the nearest points, and determine whether it's within snapping range and if we should snap to it std::vector<double>::const_iterator np = anp.begin(); unsigned int index = 0; for (; np != anp.end(); ++np, index++) { - Geom::Curve const *curve = &((*it_pv).at_index(index)); + Geom::Curve const *curve = &(it_pv->at(index)); Geom::Point const sp_doc = curve->pointAt(*np); //dt->snapindicator->set_new_debugging_point(sp_doc*dt->doc2dt()); bool c1 = true; @@ -622,10 +624,13 @@ void Inkscape::ObjectSnapper::_snapPathsConstrained(IntermSnapResults &isr, // PS: Because the paths we're about to snap to are all expressed relative to document coordinate system, we will have // to convert the snapper coordinates from the desktop coordinates to document coordinates - std::vector<Geom::Path> constraint_path; + Geom::PathVector constraint_path; if (c.isCircular()) { Geom::Circle constraint_circle(dt->dt2doc(c.getPoint()), c.getRadius()); - constraint_circle.getPath(constraint_path); + Geom::PathBuilder pb; + pb.feed(constraint_circle); + pb.flush(); + constraint_path = pb.peek(); } else { Geom::Path constraint_line; constraint_line.start(p_min_on_cl); diff --git a/src/object-test.h b/src/object-test.h index 06363c372..4f0be3251 100644 --- a/src/object-test.h +++ b/src/object-test.h @@ -204,7 +204,8 @@ public: assert(n_group != NULL); begin = clock(); - sp_item_group_ungroup(n_group, NULL, false); + std::vector<SPItem*> ch; + sp_item_group_ungroup(n_group, ch, false); end = clock(); std::cout << "Took " << double(end - begin) / double(CLOCKS_PER_SEC) << " seconds to ungroup a <g> with " << num_elements << " elements\n"; diff --git a/src/path-chemistry.cpp b/src/path-chemistry.cpp index 5f6e1495b..7bd5b6298 100644 --- a/src/path-chemistry.cpp +++ b/src/path-chemistry.cpp @@ -44,13 +44,22 @@ using Inkscape::DocumentUndo; + +inline bool less_than_items(SPItem const *first, SPItem const *second) +{ + return sp_repr_compare_position(first->getRepr(), + second->getRepr())<0; +} + void sp_selected_path_combine(SPDesktop *desktop) { Inkscape::Selection *selection = desktop->getSelection(); SPDocument *doc = desktop->getDocument(); + + std::vector<SPItem*> items(selection->itemList()); - if (g_slist_length((GSList *) selection->itemList()) < 1) { + if (items.size() < 1) { desktop->getMessageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select <b>object(s)</b> to combine.")); return; } @@ -59,28 +68,23 @@ sp_selected_path_combine(SPDesktop *desktop) // set "busy" cursor desktop->setWaitingCursor(); - GSList *items = g_slist_copy((GSList *) selection->itemList()); - items = sp_degroup_list (items); // descend into any groups in selection - GSList *to_paths = NULL; - for (GSList *i = items; i != NULL; i = i->next) { - SPItem *item = (SPItem *) i->data; - if (!dynamic_cast<SPPath *>(item) && !dynamic_cast<SPGroup *>(item)) { - to_paths = g_slist_prepend(to_paths, item); + std::vector<SPItem*> to_paths; + for (std::vector<SPItem*>::const_reverse_iterator i = items.rbegin(); i != items.rend(); i++) { + if (!dynamic_cast<SPPath *>(*i) && !dynamic_cast<SPGroup *>(*i)) { + to_paths.push_back(*i); } } - GSList *converted = NULL; - bool did = sp_item_list_to_curves(to_paths, &items, &converted); - g_slist_free(to_paths); - for (GSList *i = converted; i != NULL; i = i->next) - items = g_slist_prepend(items, doc->getObjectByRepr((Inkscape::XML::Node*)(i->data))); + std::vector<Inkscape::XML::Node*> converted; + bool did = sp_item_list_to_curves(to_paths, items, converted); + for (std::vector<Inkscape::XML::Node*>::const_iterator i = converted.begin(); i != converted.end(); i++) + items.push_back((SPItem*)doc->getObjectByRepr(*i)); items = sp_degroup_list (items); // converting to path may have added more groups, descend again - 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 + sort(items.begin(),items.end(),less_than_items); + assert(!items.empty()); // 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; @@ -97,9 +101,9 @@ sp_selected_path_combine(SPDesktop *desktop) selection->clear(); } - for (GSList *i = items; i != NULL; i = i->next) { // going from top to bottom + for (std::vector<SPItem*>::const_reverse_iterator i = items.rbegin(); i != items.rend(); i++){ - SPItem *item = (SPItem *) i->data; + SPItem *item = *i; SPPath *path = dynamic_cast<SPPath *>(item); if (!path) { continue; @@ -136,7 +140,6 @@ sp_selected_path_combine(SPDesktop *desktop) } } - g_slist_free(items); if (did) { first->deleteObject(false); @@ -200,11 +203,10 @@ sp_selected_path_break_apart(SPDesktop *desktop) bool did = false; - for (GSList *items = g_slist_copy((GSList *) selection->itemList()); - items != NULL; - items = items->next) { + std::vector<SPItem*> itemlist(selection->itemList()); + for (std::vector<SPItem*>::const_iterator i = itemlist.begin(); i != itemlist.end(); i++){ - SPItem *item = (SPItem *) items->data; + SPItem *item = *i; SPPath *path = dynamic_cast<SPPath *>(item); if (!path) { @@ -241,7 +243,7 @@ sp_selected_path_break_apart(SPDesktop *desktop) curve->unref(); - GSList *reprs = NULL; + std::vector<Inkscape::XML::Node*> reprs; for (GSList *l = list; l != NULL; l = l->next) { curve = (SPCurve *) l->data; @@ -267,14 +269,12 @@ sp_selected_path_break_apart(SPDesktop *desktop) if (l == list) repr->setAttribute("id", id); - reprs = g_slist_prepend (reprs, repr); + reprs.push_back(repr); Inkscape::GC::release(repr); } - selection->setReprList(reprs); - g_slist_free(reprs); g_slist_free(list); g_free(style); g_free(path_effect); @@ -307,18 +307,15 @@ sp_selected_path_to_curves(Inkscape::Selection *selection, SPDesktop *desktop, b desktop->setWaitingCursor(); } - GSList *selected = g_slist_copy((GSList *) selection->itemList()); - GSList *to_select = NULL; + std::vector<SPItem*> selected(selection->itemList()); + std::vector<Inkscape::XML::Node*> to_select; selection->clear(); - GSList *items = g_slist_copy(selected); + std::vector<SPItem*> items(selected); - did = sp_item_list_to_curves(items, &selected, &to_select); + did = sp_item_list_to_curves(items, selected, to_select); - g_slist_free (items); selection->setReprList(to_select); selection->addList(selected); - g_slist_free (to_select); - g_slist_free (selected); if (interactive && desktop) { desktop->clearWaitingCursor(); @@ -341,33 +338,24 @@ void sp_selected_to_lpeitems(SPDesktop *desktop) return; } - GSList *selected = g_slist_copy((GSList *) selection->itemList()); - GSList *to_select = NULL; + std::vector<SPItem*> selected(selection->itemList()); + std::vector<Inkscape::XML::Node*> to_select; selection->clear(); - GSList *items = g_slist_copy(selected); + std::vector<SPItem*> items(selected); + - sp_item_list_to_curves(items, &selected, &to_select, true); + sp_item_list_to_curves(items, selected, to_select, true); - g_slist_free(items); - items = 0; selection->setReprList(to_select); selection->addList(selected); - g_slist_free(to_select); - to_select = 0; - g_slist_free(selected); - selected = 0; } bool -sp_item_list_to_curves(const GSList *items, GSList **selected, GSList **to_select, bool skip_all_lpeitems) +sp_item_list_to_curves(const std::vector<SPItem*> &items, std::vector<SPItem*>& selected, std::vector<Inkscape::XML::Node*> &to_select, bool skip_all_lpeitems) { bool did = false; - - for (; - items != NULL; - items = items->next) { - - SPItem *item = dynamic_cast<SPItem *>(static_cast<SPObject *>(items->data)); + for (std::vector<SPItem*>::const_iterator i = items.begin(); i != items.end(); i++){ + SPItem *item = *i; g_assert(item != NULL); SPDocument *document = item->document; @@ -398,9 +386,9 @@ sp_item_list_to_curves(const GSList *items, GSList **selected, GSList **to_selec Inkscape::XML::Node *repr = box3d_convert_to_group(box)->getRepr(); if (repr) { - *to_select = g_slist_prepend (*to_select, repr); + to_select.insert(to_select.begin(),repr); did = true; - *selected = g_slist_remove (*selected, item); + selected.erase(remove(selected.begin(), selected.end(), item), selected.end()); } continue; @@ -408,17 +396,14 @@ sp_item_list_to_curves(const GSList *items, GSList **selected, GSList **to_selec if (group) { group->removeAllPathEffects(true); - GSList *item_list = sp_item_group_item_list(group); + std::vector<SPItem*> item_list = sp_item_group_item_list(group); - GSList *item_to_select = NULL; - GSList *item_selected = NULL; + std::vector<Inkscape::XML::Node*> item_to_select; + std::vector<SPItem*> item_selected; - if (sp_item_list_to_curves(item_list, &item_selected, &item_to_select)) + if (sp_item_list_to_curves(item_list, item_selected, item_to_select)) did = true; - g_slist_free(item_list); - g_slist_free(item_to_select); - g_slist_free(item_selected); continue; } @@ -428,7 +413,7 @@ sp_item_list_to_curves(const GSList *items, GSList **selected, GSList **to_selec continue; did = true; - *selected = g_slist_remove (*selected, item); + selected.erase(remove(selected.begin(), selected.end(), item), selected.end()); // remember the position of the item gint pos = item->getRepr()->position(); @@ -470,7 +455,7 @@ sp_item_list_to_curves(const GSList *items, GSList **selected, GSList **to_selec /* Buglet: We don't re-add the (new version of the) object to the selection of any other * desktops where it was previously selected. */ - *to_select = g_slist_prepend (*to_select, repr); + to_select.insert(to_select.begin(),repr); Inkscape::GC::release(repr); } @@ -488,6 +473,9 @@ sp_selected_item_to_curved_repr(SPItem *item, guint32 /*text_grouping_policy*/) if (dynamic_cast<SPText *>(item) || dynamic_cast<SPFlowtext *>(item)) { // Special treatment for text: convert each glyph to separate path, then group the paths Inkscape::XML::Node *g_repr = xml_doc->createElement("svg:g"); + + Glib::ustring original_text; // To save original text of accessibility. + g_repr->setAttribute("transform", item->getRepr()->attribute("transform")); /* Mask */ gchar *mask_str = (gchar *) item->getRepr()->attribute("mask"); @@ -508,6 +496,8 @@ sp_selected_item_to_curved_repr(SPItem *item, guint32 /*text_grouping_policy*/) Inkscape::Text::Layout::iterator iter = te_get_layout(item)->begin(); do { + original_text += (gunichar)te_get_layout(item)->characterAt( iter ); + Inkscape::Text::Layout::iterator iter_next = iter; iter_next.nextGlyph(); // iter_next is one glyph ahead from iter if (iter == iter_next) @@ -547,6 +537,11 @@ sp_selected_item_to_curved_repr(SPItem *item, guint32 /*text_grouping_policy*/) p_repr->setAttribute("style", style_str.c_str()); g_repr->appendChild(p_repr); + + // For accessibility, store original string + if( original_text.size() > 0 ) { + g_repr->setAttribute("aria-label", original_text.c_str() ); + } Inkscape::GC::release(p_repr); if (iter == te_get_layout(item)->end()) @@ -612,9 +607,9 @@ void sp_selected_path_reverse(SPDesktop *desktop) { Inkscape::Selection *selection = desktop->getSelection(); - GSList *items = (GSList *) selection->itemList(); + std::vector<SPItem*> items = selection->itemList(); - if (!items) { + if (items.empty()) { desktop->getMessageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select <b>path(s)</b> to reverse.")); return; } @@ -626,9 +621,9 @@ sp_selected_path_reverse(SPDesktop *desktop) bool did = false; desktop->messageStack()->flash(Inkscape::IMMEDIATE_MESSAGE, _("Reversing paths...")); - for (GSList *i = items; i != NULL; i = i->next) { + for (std::vector<SPItem*>::const_iterator i = items.begin(); i != items.end(); i++){ - SPPath *path = dynamic_cast<SPPath *>(static_cast<SPObject *>(i->data)); + SPPath *path = dynamic_cast<SPPath *>(*i); if (!path) { continue; } diff --git a/src/path-chemistry.h b/src/path-chemistry.h index a2150440c..f454167a9 100644 --- a/src/path-chemistry.h +++ b/src/path-chemistry.h @@ -33,7 +33,7 @@ void sp_selected_path_to_curves (Inkscape::Selection *selection, SPDesktop *desk void sp_selected_to_lpeitems(SPDesktop *desktop); Inkscape::XML::Node *sp_selected_item_to_curved_repr(SPItem *item, guint32 text_grouping_policy); void sp_selected_path_reverse (SPDesktop *desktop); -bool sp_item_list_to_curves(const GSList *items, GSList **selected, GSList **to_select, bool skip_all_lpeitems = false); +bool sp_item_list_to_curves(const std::vector<SPItem*> &items, std::vector<SPItem*> &selected, std::vector<Inkscape::XML::Node*> &to_select, bool skip_all_lpeitems = false); #endif // SEEN_PATH_CHEMISTRY_H diff --git a/src/persp3d-reference.cpp b/src/persp3d-reference.cpp index 895eac6f2..4526a8d8f 100644 --- a/src/persp3d-reference.cpp +++ b/src/persp3d-reference.cpp @@ -35,7 +35,8 @@ Persp3DReference::~Persp3DReference(void) bool Persp3DReference::_acceptObject(SPObject *obj) const { - return SP_IS_PERSP3D(obj); + return SP_IS_PERSP3D(obj) && URIReference::_acceptObject(obj); +; /* effic: Don't bother making this an inline function: _acceptObject is a virtual function, typically called from a context where the runtime type is not known at compile time. */ } diff --git a/src/preferences-skeleton.h b/src/preferences-skeleton.h index 66a3e47d4..f4a08b928 100644 --- a/src/preferences-skeleton.h +++ b/src/preferences-skeleton.h @@ -95,7 +95,7 @@ static char const preferences_skeleton[] = " <eventcontext id=\"pencil\" tolerance=\"4.0\" selcue=\"1\" style=\"stroke-width:1px;\" usecurrent=\"0\" average_all_sketches=\"1\"/>\n" " <eventcontext id=\"pen\" mode=\"drag\" selcue=\"1\" style=\"stroke-width:1px;\" usecurrent=\"0\"/>\n" " </group>\n" -" <eventcontext id=\"calligraphic\" style=\"fill:black;fill-opacity:1;fill-rule:nonzero;stroke:none;\"\n" +" <eventcontext id=\"calligraphic\" style=\"fill:black;fill-opacity:1;stroke:none;\"\n" " mass=\"2\" angle=\"30\" width=\"15\" thinning=\"10\" flatness=\"90\" cap_rounding=\"0.0\" usecurrent=\"1\"\n" " tracebackground=\"0\" usepressure=\"1\" usetilt=\"0\" keep_selected=\"1\">\n" " <group id=\"preset\">\n" diff --git a/src/rdf.cpp b/src/rdf.cpp index 16344e520..938dc60c6 100644 --- a/src/rdf.cpp +++ b/src/rdf.cpp @@ -39,7 +39,7 @@ </Agent></dc:rights> <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> <dc:source rdf:resource="source"/> - <license rdf:resource="http://creativecommons.org/licenses/by/2.0/" + <license rdf:resource="http://creativecommons.org/licenses/by/4.0/" /> </Work> @@ -173,32 +173,32 @@ struct rdf_double_t rdf_license_ofl [] = { struct rdf_license_t rdf_licenses [] = { { N_("CC Attribution"), - "http://creativecommons.org/licenses/by/3.0/", + "http://creativecommons.org/licenses/by/4.0/", rdf_license_cc_a, }, { N_("CC Attribution-ShareAlike"), - "http://creativecommons.org/licenses/by-sa/3.0/", + "http://creativecommons.org/licenses/by-sa/4.0/", rdf_license_cc_a_sa, }, { N_("CC Attribution-NoDerivs"), - "http://creativecommons.org/licenses/by-nd/3.0/", + "http://creativecommons.org/licenses/by-nd/4.0/", rdf_license_cc_a_nd, }, { N_("CC Attribution-NonCommercial"), - "http://creativecommons.org/licenses/by-nc/3.0/", + "http://creativecommons.org/licenses/by-nc/4.0/", rdf_license_cc_a_nc, }, { N_("CC Attribution-NonCommercial-ShareAlike"), - "http://creativecommons.org/licenses/by-nc-sa/3.0/", + "http://creativecommons.org/licenses/by-nc-sa/4.0/", rdf_license_cc_a_nc_sa, }, { N_("CC Attribution-NonCommercial-NoDerivs"), - "http://creativecommons.org/licenses/by-nc-nd/3.0/", + "http://creativecommons.org/licenses/by-nc-nd/4.0/", rdf_license_cc_a_nc_nd, }, diff --git a/src/removeoverlap.cpp b/src/removeoverlap.cpp index ba5740e55..9ea75de0d 100644 --- a/src/removeoverlap.cpp +++ b/src/removeoverlap.cpp @@ -38,20 +38,19 @@ namespace { * such that rectangular bounding boxes are separated by at least xGap * horizontally and yGap vertically */ -void removeoverlap(GSList const *const items, double const xGap, double const yGap) { - using Inkscape::Util::GSListConstIterator; - std::list<SPItem *> selected; - selected.insert<GSListConstIterator<SPItem *> >(selected.end(), items, NULL); +void removeoverlap(std::vector<SPItem*> const &items, double const xGap, double const yGap) { + std::vector<SPItem*> selected(items); std::vector<Record> records; std::vector<Rectangle *> rs; Geom::Point const gap(xGap, yGap); - for (std::list<SPItem *>::iterator it(selected.begin()); + for (std::vector<SPItem*>::iterator it(selected.begin()); it != selected.end(); ++it) { + SPItem* item = *it; using Geom::X; using Geom::Y; - Geom::OptRect item_box((*it)->desktopVisualBounds()); + Geom::OptRect item_box((item)->desktopVisualBounds()); if (item_box) { Geom::Point min(item_box->min() - .5*gap); Geom::Point max(item_box->max() + .5*gap); @@ -67,7 +66,7 @@ void removeoverlap(GSList const *const items, double const xGap, double const yG min[Y] = max[Y] = (min[Y] + max[Y])/2; } Rectangle *vspc_rect = new Rectangle(min[X], max[X], min[Y], max[Y]); - records.push_back(Record(*it, item_box->midpoint(), vspc_rect)); + records.push_back(Record(item, item_box->midpoint(), vspc_rect)); rs.push_back(vspc_rect); } } diff --git a/src/removeoverlap.h b/src/removeoverlap.h index 7109c9513..d873663d1 100644 --- a/src/removeoverlap.h +++ b/src/removeoverlap.h @@ -15,6 +15,6 @@ typedef struct _GSList GSList; -void removeoverlap(GSList const *items, double xGap, double yGap); +void removeoverlap(std::vector<SPItem*> const &items, double xGap, double yGap); #endif // SEEN_REMOVEOVERLAP_H diff --git a/src/selcue.cpp b/src/selcue.cpp index d2fa0970a..c73219b7d 100644 --- a/src/selcue.cpp +++ b/src/selcue.cpp @@ -96,15 +96,16 @@ void Inkscape::SelCue::_updateItemBboxes(Inkscape::Preferences *prefs) void Inkscape::SelCue::_updateItemBboxes(gint mode, int prefs_bbox) { - GSList const *items = _selection->itemList(); - if (_item_bboxes.size() != g_slist_length((GSList *) items)) { + const std::vector<SPItem*> items = _selection->itemList(); + if (_item_bboxes.size() != items.size()) { _newItemBboxes(); return; } int bcount = 0; - for (GSList const *l = _selection->itemList(); l != NULL; l = l->next) { - SPItem *item = static_cast<SPItem *>(l->data); + std::vector<SPItem*> ll=_selection->itemList(); + for (std::vector<SPItem*>::const_iterator l = ll.begin(); l != ll.end(); l++) { + SPItem *item = *l; SPCanvasItem* box = _item_bboxes[bcount ++]; if (box) { @@ -145,8 +146,9 @@ void Inkscape::SelCue::_newItemBboxes() int prefs_bbox = prefs->getBool("/tools/bounding_box"); - for (GSList const *l = _selection->itemList(); l != NULL; l = l->next) { - SPItem *item = static_cast<SPItem *>(l->data); + std::vector<SPItem*> ll=_selection->itemList(); + for (std::vector<SPItem*>::const_iterator l = ll.begin(); l != ll.end(); l++) { + SPItem *item = *l; Geom::OptRect const b = (prefs_bbox == 0) ? item->desktopVisualBounds() : item->desktopGeometricBounds(); @@ -199,8 +201,9 @@ void Inkscape::SelCue::_newTextBaselines() } _text_baselines.clear(); - for (GSList const *l = _selection->itemList(); l != NULL; l = l->next) { - SPItem *item = static_cast<SPItem *>(l->data); + std::vector<SPItem*> ll = _selection->itemList(); + for (std::vector<SPItem*>::const_iterator l=ll.begin();l!=ll.end();l++) { + SPItem *item = *l; SPCanvasItem* baseline_point = NULL; if (SP_IS_TEXT(item) || SP_IS_FLOWTEXT(item)) { // visualize baseline diff --git a/src/selection-chemistry.cpp b/src/selection-chemistry.cpp index 5e8fd5e07..3999156db 100644 --- a/src/selection-chemistry.cpp +++ b/src/selection-chemistry.cpp @@ -107,6 +107,7 @@ SPCycleType SP_CYCLING = SP_CYCLE_FOCUS; #include "live_effects/effect.h" #include "live_effects/effect-enum.h" #include "live_effects/parameter/originalpath.h" +#include "layer-manager.h" #include "enums.h" #include "sp-item-group.h" @@ -279,25 +280,21 @@ void SelectionHelper::fixSelection(SPDesktop *dt) Inkscape::Selection *selection = dt->getSelection(); - GSList *items = NULL; + std::vector<SPItem*> items ; - GSList const *selList = selection->itemList(); + std::vector<SPItem*> const selList = selection->itemList(); - for( GSList const *i = selList; i; i = i->next ) { - SPItem *item = dynamic_cast<SPItem *>(static_cast<SPObject *>(i->data)); + for( std::vector<SPItem*>::const_reverse_iterator i = selList.rbegin(); i != selList.rend(); i++ ) { + SPItem *item = *i; if( item && !dt->isLayer(item) && (!item->isLocked())) { - items = g_slist_prepend(items, item); + items.push_back(item); } } selection->setList(items); - - if(items) { - g_slist_free(items); - } } } // namespace Inkscape @@ -307,7 +304,7 @@ void SelectionHelper::fixSelection(SPDesktop *dt) * Copies repr and its inherited css style elements, along with the accumulated transform 'full_t', * then prepends the copy to 'clip'. */ -static void sp_selection_copy_one(Inkscape::XML::Node *repr, Geom::Affine full_t, GSList **clip, Inkscape::XML::Document* xml_doc) +static void sp_selection_copy_one(Inkscape::XML::Node *repr, Geom::Affine full_t, std::vector<Inkscape::XML::Node*> &clip, Inkscape::XML::Document* xml_doc) { Inkscape::XML::Node *copy = repr->duplicate(xml_doc); @@ -323,41 +320,39 @@ static void sp_selection_copy_one(Inkscape::XML::Node *repr, Geom::Affine full_t copy->setAttribute("transform", affinestr); g_free(affinestr); - *clip = g_slist_prepend(*clip, copy); + clip.insert(clip.begin(),copy); } -static void sp_selection_copy_impl(GSList const *items, GSList **clip, Inkscape::XML::Document* xml_doc) +static void sp_selection_copy_impl(std::vector<SPItem*> const &items, std::vector<Inkscape::XML::Node*> &clip, Inkscape::XML::Document* xml_doc) { // Sort items: - GSList *sorted_items = g_slist_copy(const_cast<GSList *>(items)); - sorted_items = g_slist_sort(static_cast<GSList *>(sorted_items), (GCompareFunc) sp_object_compare_position); + std::vector<SPItem*> sorted_items(items); + sort(sorted_items.begin(),sorted_items.end(),sp_object_compare_position_bool); // Copy item reprs: - for (GSList *i = sorted_items; i != NULL; i = i->next) { - SPItem *item = dynamic_cast<SPItem *>(SP_OBJECT(i->data)); + for (std::vector<SPItem*>::const_iterator i = sorted_items.begin(); i != sorted_items.end(); i++) { + SPItem *item = *i; if (item) { sp_selection_copy_one(item->getRepr(), item->i2doc_affine(), clip, xml_doc); } else { g_assert_not_reached(); } } - - *clip = g_slist_reverse(*clip); - g_slist_free(static_cast<GSList *>(sorted_items)); + reverse(clip.begin(),clip.end()); } // TODO check if parent parameter should be changed to SPItem, of if the code should handle non-items. -static GSList *sp_selection_paste_impl(SPDocument *doc, SPObject *parent, GSList **clip) +static std::vector<Inkscape::XML::Node*> sp_selection_paste_impl(SPDocument *doc, SPObject *parent, std::vector<Inkscape::XML::Node*> &clip) { Inkscape::XML::Document *xml_doc = doc->getReprDoc(); SPItem *parentItem = dynamic_cast<SPItem *>(parent); g_assert(parentItem != NULL); - GSList *copied = NULL; + std::vector<Inkscape::XML::Node*> copied; // add objects to document - for (GSList *l = *clip; l != NULL; l = l->next) { - Inkscape::XML::Node *repr = static_cast<Inkscape::XML::Node *>(l->data); + for (std::vector<Inkscape::XML::Node*>::const_iterator l = clip.begin(); l != clip.end(); l++) { + Inkscape::XML::Node *repr = *l; Inkscape::XML::Node *copy = repr->duplicate(xml_doc); // premultiply the item transform by the accumulated parent transform in the paste layer @@ -375,19 +370,19 @@ static GSList *sp_selection_paste_impl(SPDocument *doc, SPObject *parent, GSList } parent->appendChildRepr(copy); - copied = g_slist_prepend(copied, copy); + copied.push_back(copy); Inkscape::GC::release(copy); } return copied; } -static void sp_selection_delete_impl(GSList const *items, bool propagate = true, bool propagate_descendants = true) +static void sp_selection_delete_impl(std::vector<SPItem*> const &items, bool propagate = true, bool propagate_descendants = true) { - for (GSList const *i = items ; i ; i = i->next ) { - sp_object_ref(static_cast<SPItem *>(i->data), NULL); + for (std::vector<SPItem*>::const_iterator i = items.begin(); i != items.end(); i++) { + sp_object_ref(*i, NULL); } - for (GSList const *i = items; i != NULL; i = i->next) { - SPItem *item = static_cast<SPItem *>(i->data); + for (std::vector<SPItem*>::const_iterator i = items.begin(); i != items.end(); i++) { + SPItem *item = *i; item->deleteObject(propagate, propagate_descendants); sp_object_unref(item, NULL); } @@ -414,11 +409,9 @@ void sp_selection_delete(SPDesktop *desktop) desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("<b>Nothing</b> was deleted.")); return; } - - GSList *selected = g_slist_copy(const_cast<GSList *>(selection->itemList())); + std::vector<SPItem*> selected(selection->itemList()); selection->clear(); sp_selection_delete_impl(selected); - g_slist_free(selected); desktop->currentLayer()->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); /* a tool may have set up private information in it's selection context @@ -446,7 +439,7 @@ static void add_ids_recursive(std::vector<const gchar *> &ids, SPObject *obj) } } -void sp_selection_duplicate(SPDesktop *desktop, bool suppressDone) +void sp_selection_duplicate(SPDesktop *desktop, bool suppressDone, bool duplicateLayer) { if (desktop == NULL) { return; @@ -457,20 +450,24 @@ void sp_selection_duplicate(SPDesktop *desktop, bool suppressDone) Inkscape::Selection *selection = desktop->getSelection(); // check if something is selected - if (selection->isEmpty()) { + if (selection->isEmpty() && !duplicateLayer) { desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select <b>object(s)</b> to duplicate.")); return; } + std::vector<Inkscape::XML::Node*> reprs(selection->reprList()); - GSList *reprs = g_slist_copy(const_cast<GSList *>(selection->reprList())); + if(duplicateLayer){ + reprs.clear(); + reprs.push_back(desktop->currentLayer()->getRepr()); + } selection->clear(); // sorting items from different parents sorts each parent's subset without possibly mixing // them, just what we need - reprs = g_slist_sort(reprs, (GCompareFunc) sp_repr_compare_position); + sort(reprs.begin(),reprs.end(),sp_repr_compare_position_bool); - GSList *newsel = NULL; + std::vector<Inkscape::XML::Node*> newsel; std::vector<const gchar *> old_ids; std::vector<const gchar *> new_ids; @@ -478,12 +475,15 @@ void sp_selection_duplicate(SPDesktop *desktop, bool suppressDone) bool relink_clones = prefs->getBool("/options/relinkclonesonduplicate/value"); const bool fork_livepatheffects = prefs->getBool("/options/forklpeonduplicate/value", true); - while (reprs) { - Inkscape::XML::Node *old_repr = static_cast<Inkscape::XML::Node *>(reprs->data); + for(std::vector<Inkscape::XML::Node*>::const_iterator i=reprs.begin();i!=reprs.end();i++){ + Inkscape::XML::Node *old_repr = *i; Inkscape::XML::Node *parent = old_repr->parent(); Inkscape::XML::Node *copy = old_repr->duplicate(xml_doc); - parent->appendChild(copy); + if(! duplicateLayer) + parent->appendChild(copy); + else + parent->addChild(copy, old_repr); if (relink_clones) { SPObject *old_obj = doc->getObjectByRepr(old_repr); @@ -500,8 +500,7 @@ void sp_selection_duplicate(SPDesktop *desktop, bool suppressDone) } } - newsel = g_slist_prepend(newsel, copy); - reprs = g_slist_remove(reprs, reprs->data); + newsel.push_back(copy); Inkscape::GC::release(copy); } @@ -545,10 +544,14 @@ void sp_selection_duplicate(SPDesktop *desktop, bool suppressDone) DocumentUndo::done(desktop->getDocument(), SP_VERB_EDIT_DUPLICATE, _("Duplicate")); } - - selection->setReprList(newsel); - - g_slist_free(newsel); + if(!duplicateLayer) + selection->setReprList(newsel); + else{ + SPObject* new_layer = doc->getObjectByRepr(newsel[0]); + gchar* name = g_strdup_printf(_("%s copy"), new_layer->label()); + desktop->layer_manager->renameLayer( new_layer, name, TRUE ); + g_free(name); + } } void sp_edit_clear_all(Inkscape::Selection *selection) @@ -561,11 +564,10 @@ void sp_edit_clear_all(Inkscape::Selection *selection) SPGroup *group = dynamic_cast<SPGroup *>(selection->layers()->currentLayer()); g_return_if_fail(group != NULL); - GSList *items = sp_item_group_item_list(group); + std::vector<SPItem*> items = sp_item_group_item_list(group); - while (items) { - reinterpret_cast<SPObject*>(items->data)->deleteObject(); - items = g_slist_remove(items, items->data); + for(unsigned int i = 0; i < items.size(); i++){ + items[i]->deleteObject(); } DocumentUndo::done(doc, SP_VERB_EDIT_CLEAR_ALL, @@ -582,7 +584,7 @@ void sp_edit_clear_all(Inkscape::Selection *selection) * onlysensitive - TRUE includes only non-locked items * ingroups - TRUE to recursively get grouped items children */ -GSList *get_all_items(GSList *list, SPObject *from, SPDesktop *desktop, bool onlyvisible, bool onlysensitive, bool ingroups, GSList const *exclude) +std::vector<SPItem*> &get_all_items(std::vector<SPItem*> &list, SPObject *from, SPDesktop *desktop, bool onlyvisible, bool onlysensitive, bool ingroups, std::vector<SPItem*> const &exclude) { for ( SPObject *child = from->firstChild() ; child; child = child->getNext() ) { SPItem *item = dynamic_cast<SPItem *>(child); @@ -590,10 +592,10 @@ GSList *get_all_items(GSList *list, SPObject *from, SPDesktop *desktop, bool onl !desktop->isLayer(item) && (!onlysensitive || !item->isLocked()) && (!onlyvisible || !desktop->itemIsHidden(item)) && - (!exclude || !g_slist_find(const_cast<GSList *>(exclude), child)) + (exclude.empty() || exclude.end() == std::find(exclude.begin(),exclude.end(),child)) ) { - list = g_slist_prepend(list, item); + list.insert(list.begin(),item); } if (ingroups || (item && desktop->isLayer(item))) { @@ -618,9 +620,9 @@ static void sp_edit_select_all_full(SPDesktop *dt, bool force_all_layers, bool i bool onlyvisible = prefs->getBool("/options/kbselection/onlyvisible", true); bool onlysensitive = prefs->getBool("/options/kbselection/onlysensitive", true); - GSList *items = NULL; + std::vector<SPItem*> items ; - GSList const *exclude = NULL; + std::vector<SPItem*> exclude; if (invert) { exclude = selection->itemList(); } @@ -634,40 +636,38 @@ static void sp_edit_select_all_full(SPDesktop *dt, bool force_all_layers, bool i (onlyvisible && dt->itemIsHidden(dynamic_cast<SPItem *>(dt->currentLayer()))) ) return; - GSList *all_items = sp_item_group_item_list(dynamic_cast<SPGroup *>(dt->currentLayer())); + std::vector<SPItem*> all_items = sp_item_group_item_list(dynamic_cast<SPGroup *>(dt->currentLayer())); - for (GSList *i = all_items; i; i = i->next) { - SPItem *item = dynamic_cast<SPItem *>(static_cast<SPObject *>(i->data)); + for (std::vector<SPItem*>::const_reverse_iterator i=all_items.rbegin();i!=all_items.rend();i++) { + SPItem *item = *i; if (item && (!onlysensitive || !item->isLocked())) { if (!onlyvisible || !dt->itemIsHidden(item)) { if (!dt->isLayer(item)) { - if (!invert || !g_slist_find(const_cast<GSList *>(exclude), item)) { - items = g_slist_prepend(items, item); // leave it in the list + if (!invert || exclude.end() == std::find(exclude.begin(),exclude.end(),item)) { + items.push_back(item); // leave it in the list } } } } } - g_slist_free(all_items); break; } case PREFS_SELECTION_LAYER_RECURSIVE: { - items = get_all_items(NULL, dt->currentLayer(), dt, onlyvisible, onlysensitive, FALSE, exclude); + std::vector<SPItem*> x; + items = get_all_items(x, dt->currentLayer(), dt, onlyvisible, onlysensitive, FALSE, exclude); break; } default: { - items = get_all_items(NULL, dt->currentRoot(), dt, onlyvisible, onlysensitive, FALSE, exclude); + std::vector<SPItem*> x; + items = get_all_items(x, dt->currentRoot(), dt, onlyvisible, onlysensitive, FALSE, exclude); break; } } selection->setList(items); - if (items) { - g_slist_free(items); - } } void sp_edit_select_all(SPDesktop *desktop) @@ -690,16 +690,16 @@ void sp_edit_invert_in_all_layers(SPDesktop *desktop) sp_edit_select_all_full(desktop, true, true); } -static void sp_selection_group_impl(GSList *p, Inkscape::XML::Node *group, Inkscape::XML::Document *xml_doc, SPDocument *doc) { +static void sp_selection_group_impl(std::vector<Inkscape::XML::Node*> p, Inkscape::XML::Node *group, Inkscape::XML::Document *xml_doc, SPDocument *doc) { - p = g_slist_sort(p, (GCompareFunc) sp_repr_compare_position); + sort(p.begin(),p.end(),sp_repr_compare_position_bool); // Remember the position and parent of the topmost object. - gint topmost = (static_cast<Inkscape::XML::Node *>(g_slist_last(p)->data))->position(); - Inkscape::XML::Node *topmost_parent = (static_cast<Inkscape::XML::Node *>(g_slist_last(p)->data))->parent(); + gint topmost = p.back()->position(); + Inkscape::XML::Node *topmost_parent = p.back()->parent(); - while (p) { - Inkscape::XML::Node *current = static_cast<Inkscape::XML::Node *>(p->data); + for(std::vector<Inkscape::XML::Node*>::const_iterator i = p.begin(); i != p.end(); i++){ + Inkscape::XML::Node *current = *i; if (current->parent() == topmost_parent) { Inkscape::XML::Node *spnew = current->duplicate(xml_doc); @@ -708,7 +708,7 @@ static void sp_selection_group_impl(GSList *p, Inkscape::XML::Node *group, Inksc Inkscape::GC::release(spnew); topmost --; // only reduce count for those items deleted from topmost_parent } else { // move it to topmost_parent first - GSList *temp_clip = NULL; + std::vector<Inkscape::XML::Node*> temp_clip; // At this point, current may already have no item, due to its being a clone whose original is already moved away // So we copy it artificially calculating the transform from its repr->attr("transform") and the parent transform @@ -724,15 +724,15 @@ static void sp_selection_group_impl(GSList *p, Inkscape::XML::Node *group, Inksc // then, if this is clone, looking up its original in that array and pre-multiplying // it by the inverse of that original's transform diff. - sp_selection_copy_one(current, item_t, &temp_clip, xml_doc); + sp_selection_copy_one(current, item_t, temp_clip, xml_doc); sp_repr_unparent(current); // paste into topmost_parent (temporarily) - GSList *copied = sp_selection_paste_impl(doc, doc->getObjectByRepr(topmost_parent), &temp_clip); - if (temp_clip) g_slist_free(temp_clip); - if (copied) { // if success, + std::vector<Inkscape::XML::Node*> copied = sp_selection_paste_impl(doc, doc->getObjectByRepr(topmost_parent), temp_clip); + if (!temp_clip.empty())temp_clip.clear() ; + if (!copied.empty()) { // if success, // take pasted object (now in topmost_parent) - Inkscape::XML::Node *in_topmost = static_cast<Inkscape::XML::Node *>(copied->data); + Inkscape::XML::Node *in_topmost = copied.back(); // make a copy Inkscape::XML::Node *spnew = in_topmost->duplicate(xml_doc); // remove pasted @@ -740,10 +740,9 @@ static void sp_selection_group_impl(GSList *p, Inkscape::XML::Node *group, Inksc // put its copy into group group->appendChild(spnew); Inkscape::GC::release(spnew); - g_slist_free(copied); + copied.clear(); } } - p = g_slist_remove(p, current); } // Add the new group to the topmost members' parent @@ -764,9 +763,7 @@ void sp_selection_group(Inkscape::Selection *selection, SPDesktop *desktop) return; } - GSList const *l = const_cast<GSList *>(selection->reprList()); - - GSList *p = g_slist_copy(const_cast<GSList *>(l)); + std::vector<Inkscape::XML::Node*> p (selection->reprList()); selection->clear(); @@ -802,11 +799,11 @@ void sp_selection_ungroup(Inkscape::Selection *selection, SPDesktop *desktop) } // first check whether there is anything to ungroup - GSList *old_select = const_cast<GSList *>(selection->itemList()); - GSList *new_select = NULL; + std::vector<SPItem*> old_select = selection->itemList(); + std::vector<SPItem*> new_select; GSList *groups = NULL; - for (GSList *item = old_select; item; item = item->next) { - SPItem *obj = static_cast<SPItem*>(item->data); + for (std::vector<SPItem*>::const_iterator item = old_select.begin(); item!=old_select.end(); item++) { + SPItem *obj = *item; if (dynamic_cast<SPGroup *>(obj)) { groups = g_slist_prepend(groups, obj); } @@ -818,14 +815,14 @@ void sp_selection_ungroup(Inkscape::Selection *selection, SPDesktop *desktop) return; } - GSList *items = g_slist_copy(old_select); + std::vector<SPItem*> items(old_select); selection->clear(); // If any of the clones refer to the groups, unlink them and replace them with successors // in the items list. GSList *clones_to_unlink = NULL; - for (GSList *item = items; item; item = item->next) { - SPUse *use = dynamic_cast<SPUse *>(static_cast<SPItem *>(item->data)); + for (std::vector<SPItem*>::const_iterator item = items.begin(); item != items.end(); item++) { + SPUse *use = dynamic_cast<SPUse *>(*item); SPItem *original = use; while (dynamic_cast<SPUse *>(original)) { @@ -833,7 +830,7 @@ void sp_selection_ungroup(Inkscape::Selection *selection, SPDesktop *desktop) } if (g_slist_find(groups, original) != NULL) { - clones_to_unlink = g_slist_prepend(clones_to_unlink, item->data); + clones_to_unlink = g_slist_prepend(clones_to_unlink, *item); } } @@ -844,57 +841,53 @@ void sp_selection_ungroup(Inkscape::Selection *selection, SPDesktop *desktop) for (GSList *item = clones_to_unlink; item; item = item->next) { SPUse *use = static_cast<SPUse *>(item->data); - GSList *items_node = g_slist_find(items, item->data); - items_node->data = use->unlink(); + std::vector<SPItem*>::iterator items_node = std::find(items.begin(),items.end(), item->data); + *items_node = use->unlink(); } g_slist_free(clones_to_unlink); // do the actual work - for (GSList *item = items; item; item = item->next) { - SPItem *obj = static_cast<SPItem *>(item->data); + for (std::vector<SPItem*>::iterator item = items.begin(); item != items.end(); item++) { + SPItem *obj = *item; // ungroup only the groups marked earlier - if (g_slist_find(groups, item->data) != NULL) { - GSList *children = NULL; - sp_item_group_ungroup(dynamic_cast<SPGroup *>(obj), &children, false); + if (g_slist_find(groups, *item) != NULL) { + std::vector<SPItem*> children; + sp_item_group_ungroup(dynamic_cast<SPGroup *>(obj), children, false); // add the items resulting from ungrouping to the selection - new_select = g_slist_concat(new_select, children); - item->data = NULL; // zero out the original pointer, which is no longer valid + new_select.insert(new_select.end(),children.begin(),children.end()); + *item = NULL; // zero out the original pointer, which is no longer valid } else { // if not a group, keep in the selection - new_select = g_slist_append(new_select, item->data); + new_select.push_back(*item); } } selection->addList(new_select); - g_slist_free(new_select); - g_slist_free(items); DocumentUndo::done(selection->layers()->getDocument(), SP_VERB_SELECTION_UNGROUP, _("Ungroup")); } /** Replace all groups in the list with their member objects, recursively; returns a new list, frees old */ -GSList * -sp_degroup_list(GSList *items) +std::vector<SPItem*> +sp_degroup_list(std::vector<SPItem*> &items) { - GSList *out = NULL; + std::vector<SPItem*> out; bool has_groups = false; - for (GSList *item = items; item; item = item->next) { - SPGroup *group = dynamic_cast<SPGroup *>(static_cast<SPObject *>(item->data)); + for (std::vector<SPItem*>::const_iterator item=items.begin();item!=items.end();item++) { + SPGroup *group = dynamic_cast<SPGroup *>(*item); if (!group) { - out = g_slist_prepend(out, item->data); + out.push_back(*item); } else { has_groups = true; - GSList *members = sp_item_group_item_list(group); - for (GSList *member = members; member; member = member->next) { - out = g_slist_prepend(out, member->data); + std::vector<SPItem*> members = sp_item_group_item_list(group); + for (std::vector<SPItem*>::const_iterator member=members.begin();member!=members.end();member++) { + out.push_back(*member); } - g_slist_free(members); + members.clear(); } } - out = g_slist_reverse(out); - g_slist_free(items); if (has_groups) { // recurse if we unwrapped a group - it may have contained others out = sp_degroup_list(out); @@ -906,18 +899,19 @@ sp_degroup_list(GSList *items) /** If items in the list have a common parent, return it, otherwise return NULL */ static SPGroup * -sp_item_list_common_parent_group(GSList const *items) +sp_item_list_common_parent_group(std::vector<SPItem*> const items) { - if (!items) { + if (items.empty()) { return NULL; } - SPObject *parent = SP_OBJECT(items->data)->parent; + SPObject *parent = items[0]->parent; // Strictly speaking this CAN happen, if user selects <svg> from Inkscape::XML editor if (!dynamic_cast<SPGroup *>(parent)) { return NULL; } - for (items = items->next; items; items = items->next) { - if (SP_OBJECT(items->data)->parent != parent) { + for (std::vector<SPItem*>::const_iterator item=items.begin();item!=items.end();item++) { + if((*item)==items[0])continue; + if ((*item)->parent != parent) { return NULL; } } @@ -927,13 +921,13 @@ sp_item_list_common_parent_group(GSList const *items) /** Finds out the minimum common bbox of the selected items. */ static Geom::OptRect -enclose_items(GSList const *items) +enclose_items(std::vector<SPItem*> const &items) { - g_assert(items != NULL); + g_assert(!items.empty()); Geom::OptRect r; - for (GSList const *i = items; i; i = i->next) { - r.unionWith(static_cast<SPItem *>(i->data)->desktopVisualBounds()); + for (std::vector<SPItem*>::const_iterator i = items.begin();i!=items.end();i++) { + r.unionWith((*i)->desktopVisualBounds()); } return r; } @@ -948,11 +942,17 @@ static SPObject *prev_sibling(SPObject *child) return prev; } +bool sp_item_repr_compare_position_bool(SPObject const *first, SPObject const *second) +{ + return sp_repr_compare_position(((SPItem*)first)->getRepr(), + ((SPItem*)second)->getRepr())<0; +} + void sp_selection_raise(Inkscape::Selection *selection, SPDesktop *desktop) { - GSList const *items = const_cast<GSList *>(selection->itemList()); - if (!items) { + std::vector<SPItem*> items= selection->itemList(); + if (items.empty()) { selection_display_message(desktop, Inkscape::WARNING_MESSAGE, _("Select <b>object(s)</b> to raise.")); return; } @@ -966,16 +966,16 @@ sp_selection_raise(Inkscape::Selection *selection, SPDesktop *desktop) Inkscape::XML::Node *grepr = const_cast<Inkscape::XML::Node *>(group->getRepr()); /* Construct reverse-ordered list of selected children. */ - GSList *rev = g_slist_copy(const_cast<GSList *>(items)); - rev = g_slist_sort(rev, (GCompareFunc) sp_item_repr_compare_position); + std::vector<SPItem*> rev(items); + sort(rev.begin(),rev.end(),sp_item_repr_compare_position_bool); // Determine the common bbox of the selected items. Geom::OptRect selected = enclose_items(items); // Iterate over all objects in the selection (starting from top). if (selected) { - while (rev) { - SPObject *child = reinterpret_cast<SPObject*>(rev->data); + for (std::vector<SPItem*>::const_iterator item=rev.begin();item!=rev.end();item++) { + SPObject *child = *item; // for each selected object, find the next sibling for (SPObject *newref = child->next; newref; newref = newref->next) { // if the sibling is an item AND overlaps our selection, @@ -984,7 +984,7 @@ sp_selection_raise(Inkscape::Selection *selection, SPDesktop *desktop) Geom::OptRect newref_bbox = newItem->desktopVisualBounds(); if ( newref_bbox && selected->intersects(*newref_bbox) ) { // AND if it's not one of our selected objects, - if (!g_slist_find(const_cast<GSList *>(items), newref)) { + if ( std::find(items.begin(),items.end(),newref)==items.end()) { // move the selected object after that sibling grepr->changeOrder(child->getRepr(), newref->getRepr()); } @@ -992,12 +992,8 @@ sp_selection_raise(Inkscape::Selection *selection, SPDesktop *desktop) } } } - rev = g_slist_remove(rev, child); } - } else { - g_slist_free(rev); } - DocumentUndo::done(selection->layers()->getDocument(), SP_VERB_SELECTION_RAISE, //TRANSLATORS: "Raise" means "to raise an object" in the undo history C_("Undo action", "Raise")); @@ -1012,7 +1008,7 @@ void sp_selection_raise_to_top(Inkscape::Selection *selection, SPDesktop *deskto return; } - GSList const *items = const_cast<GSList *>(selection->itemList()); + std::vector<SPItem*> items = selection->itemList(); SPGroup const *group = sp_item_list_common_parent_group(items); if (!group) { @@ -1020,24 +1016,22 @@ void sp_selection_raise_to_top(Inkscape::Selection *selection, SPDesktop *deskto return; } - GSList *rl = g_slist_copy(const_cast<GSList *>(selection->reprList())); - rl = g_slist_sort(rl, (GCompareFunc) sp_repr_compare_position); + std::vector<Inkscape::XML::Node*> rl(selection->reprList()); + sort(rl.begin(),rl.end(),sp_repr_compare_position_bool); - for (GSList *l = rl; l != NULL; l = l->next) { - Inkscape::XML::Node *repr = static_cast<Inkscape::XML::Node *>(l->data); + for (std::vector<Inkscape::XML::Node*>::const_iterator l=rl.begin(); l!=rl.end();l++) { + Inkscape::XML::Node *repr =(*l); repr->setPosition(-1); } - g_slist_free(rl); - DocumentUndo::done(document, SP_VERB_SELECTION_TO_FRONT, _("Raise to top")); } void sp_selection_lower(Inkscape::Selection *selection, SPDesktop *desktop) { - GSList const *items = const_cast<GSList *>(selection->itemList()); - if (!items) { + std::vector<SPItem*> items = selection->itemList(); + if (items.empty()) { selection_display_message(desktop, Inkscape::WARNING_MESSAGE, _("Select <b>object(s)</b> to lower.")); return; } @@ -1054,14 +1048,13 @@ void sp_selection_lower(Inkscape::Selection *selection, SPDesktop *desktop) Geom::OptRect selected = enclose_items(items); /* Construct direct-ordered list of selected children. */ - GSList *rev = g_slist_copy(const_cast<GSList *>(items)); - rev = g_slist_sort(rev, (GCompareFunc) sp_item_repr_compare_position); - rev = g_slist_reverse(rev); + std::vector<SPItem*> rev(items); + sort(rev.begin(),rev.end(),sp_item_repr_compare_position_bool); // Iterate over all objects in the selection (starting from top). if (selected) { - while (rev) { - SPObject *child = reinterpret_cast<SPObject*>(rev->data); + for (std::vector<SPItem*>::const_reverse_iterator item=rev.rbegin();item!=rev.rend();item++) { + SPObject *child = *item; // for each selected object, find the prev sibling for (SPObject *newref = prev_sibling(child); newref; newref = prev_sibling(newref)) { // if the sibling is an item AND overlaps our selection, @@ -1070,7 +1063,7 @@ void sp_selection_lower(Inkscape::Selection *selection, SPDesktop *desktop) Geom::OptRect ref_bbox = newItem->desktopVisualBounds(); if ( ref_bbox && selected->intersects(*ref_bbox) ) { // AND if it's not one of our selected objects, - if (!g_slist_find(const_cast<GSList *>(items), newref)) { + if (items.end()==std::find(items.begin(),items.end(),newref)) { // move the selected object before that sibling SPObject *put_after = prev_sibling(newref); if (put_after) @@ -1082,10 +1075,7 @@ void sp_selection_lower(Inkscape::Selection *selection, SPDesktop *desktop) } } } - rev = g_slist_remove(rev, child); } - } else { - g_slist_free(rev); } DocumentUndo::done(selection->layers()->getDocument(), SP_VERB_SELECTION_LOWER, @@ -1102,7 +1092,7 @@ void sp_selection_lower_to_bottom(Inkscape::Selection *selection, SPDesktop *des return; } - GSList const *items = const_cast<GSList *>(selection->itemList()); + std::vector<SPItem*> items =selection->itemList(); SPGroup const *group = sp_item_list_common_parent_group(items); if (!group) { @@ -1110,15 +1100,13 @@ void sp_selection_lower_to_bottom(Inkscape::Selection *selection, SPDesktop *des return; } - GSList *rl; - rl = g_slist_copy(const_cast<GSList *>(selection->reprList())); - rl = g_slist_sort(rl, (GCompareFunc) sp_repr_compare_position); - rl = g_slist_reverse(rl); + std::vector<Inkscape::XML::Node*> rl(selection->reprList()); + sort(rl.begin(),rl.end(),sp_repr_compare_position_bool); - for (GSList *l = rl; l != NULL; l = l->next) { + for (std::vector<Inkscape::XML::Node*>::const_reverse_iterator l=rl.rbegin();l!=rl.rend();l++) { gint minpos; SPObject *pp, *pc; - Inkscape::XML::Node *repr = static_cast<Inkscape::XML::Node *>(l->data); + Inkscape::XML::Node *repr = (*l); pp = document->getObjectByRepr(repr->parent()); minpos = 0; g_assert(dynamic_cast<SPGroup *>(pp)); @@ -1130,8 +1118,6 @@ void sp_selection_lower_to_bottom(Inkscape::Selection *selection, SPDesktop *des repr->setPosition(minpos); } - g_slist_free(rl); - DocumentUndo::done(document, SP_VERB_SELECTION_TO_BACK, _("Lower to bottom")); } @@ -1269,9 +1255,9 @@ void sp_selection_remove_livepatheffect(SPDesktop *desktop) desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select <b>object(s)</b> to remove live path effects from.")); return; } - - for ( GSList const *itemlist = selection->itemList(); itemlist != NULL; itemlist = g_slist_next(itemlist) ) { - SPItem *item = reinterpret_cast<SPItem*>(itemlist->data); + std::vector<SPItem*> list=selection->itemList(); + for ( std::vector<SPItem*>::const_iterator itemlist=list.begin();itemlist!=list.end();itemlist++) { + SPItem *item = *itemlist; sp_selection_remove_livepatheffect_impl(item); @@ -1325,10 +1311,10 @@ void sp_selection_paste_size_separately(SPDesktop *desktop, bool apply_x, bool a * Ensures that the clones of objects are not modified when moving objects between layers. * Calls the same function as ungroup */ -void sp_selection_change_layer_maintain_clones(GSList const *items,SPObject *where) +void sp_selection_change_layer_maintain_clones(std::vector<SPItem*> const &items,SPObject *where) { - for (const GSList *i = items; i != NULL; i = i->next) { - SPItem *item = dynamic_cast<SPItem *>(SP_OBJECT(i->data)); + for (std::vector<SPItem*>::const_iterator i = items.begin(); i != items.end(); i++) { + SPItem *item = *i; if (item) { SPItem *oldparent = dynamic_cast<SPItem *>(item->parent); SPItem *newparent = dynamic_cast<SPItem *>(where); @@ -1350,26 +1336,24 @@ void sp_selection_to_next_layer(SPDesktop *dt, bool suppressDone) return; } - GSList const *items = g_slist_copy(const_cast<GSList *>(selection->itemList())); + std::vector<SPItem*> items(selection->itemList()); bool no_more = false; // Set to true, if no more layers above SPObject *next=Inkscape::next_layer(dt->currentRoot(), dt->currentLayer()); if (next) { sp_selection_change_layer_maintain_clones(items,next); - GSList *temp_clip = NULL; - sp_selection_copy_impl(items, &temp_clip, dt->doc()->getReprDoc()); + std::vector<Inkscape::XML::Node*> temp_clip; + sp_selection_copy_impl(items, temp_clip, dt->doc()->getReprDoc()); sp_selection_delete_impl(items, false, false); next=Inkscape::next_layer(dt->currentRoot(), dt->currentLayer()); // Fixes bug 1482973: crash while moving layers - GSList *copied; + std::vector<Inkscape::XML::Node*> copied; if (next) { - copied = sp_selection_paste_impl(dt->getDocument(), next, &temp_clip); + copied = sp_selection_paste_impl(dt->getDocument(), next, temp_clip); } else { - copied = sp_selection_paste_impl(dt->getDocument(), dt->currentLayer(), &temp_clip); + copied = sp_selection_paste_impl(dt->getDocument(), dt->currentLayer(), temp_clip); no_more = true; } - selection->setReprList((GSList const *) copied); - g_slist_free(copied); - if (temp_clip) g_slist_free(temp_clip); + selection->setReprList(copied); if (next) dt->setCurrentLayer(next); if ( !suppressDone ) { DocumentUndo::done(dt->getDocument(), SP_VERB_LAYER_MOVE_TO_NEXT, @@ -1383,7 +1367,6 @@ void sp_selection_to_next_layer(SPDesktop *dt, bool suppressDone) dt->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No more layers above.")); } - g_slist_free(const_cast<GSList *>(items)); } void sp_selection_to_prev_layer(SPDesktop *dt, bool suppressDone) @@ -1396,26 +1379,24 @@ void sp_selection_to_prev_layer(SPDesktop *dt, bool suppressDone) return; } - GSList const *items = g_slist_copy(const_cast<GSList *>(selection->itemList())); + const std::vector<SPItem*> items(selection->itemList()); bool no_more = false; // Set to true, if no more layers below SPObject *next=Inkscape::previous_layer(dt->currentRoot(), dt->currentLayer()); if (next) { sp_selection_change_layer_maintain_clones(items,next); - GSList *temp_clip = NULL; - sp_selection_copy_impl(items, &temp_clip, dt->doc()->getReprDoc()); // we're in the same doc, so no need to copy defs + std::vector<Inkscape::XML::Node*> temp_clip; + sp_selection_copy_impl(items, temp_clip, dt->doc()->getReprDoc()); // we're in the same doc, so no need to copy defs sp_selection_delete_impl(items, false, false); next=Inkscape::previous_layer(dt->currentRoot(), dt->currentLayer()); // Fixes bug 1482973: crash while moving layers - GSList *copied; + std::vector<Inkscape::XML::Node*> copied; if (next) { - copied = sp_selection_paste_impl(dt->getDocument(), next, &temp_clip); + copied = sp_selection_paste_impl(dt->getDocument(), next, temp_clip); } else { - copied = sp_selection_paste_impl(dt->getDocument(), dt->currentLayer(), &temp_clip); + copied = sp_selection_paste_impl(dt->getDocument(), dt->currentLayer(), temp_clip); no_more = true; } - selection->setReprList((GSList const *) copied); - g_slist_free(copied); - if (temp_clip) g_slist_free(temp_clip); + selection->setReprList( copied); if (next) dt->setCurrentLayer(next); if ( !suppressDone ) { DocumentUndo::done(dt->getDocument(), SP_VERB_LAYER_MOVE_TO_PREV, @@ -1428,8 +1409,6 @@ void sp_selection_to_prev_layer(SPDesktop *dt, bool suppressDone) if (no_more) { dt->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No more layers below.")); } - - g_slist_free(const_cast<GSList *>(items)); } void sp_selection_to_layer(SPDesktop *dt, SPObject *moveto, bool suppressDone) @@ -1442,25 +1421,22 @@ void sp_selection_to_layer(SPDesktop *dt, SPObject *moveto, bool suppressDone) return; } - GSList const *items = g_slist_copy(const_cast<GSList *>(selection->itemList())); + std::vector<SPItem*> items(selection->itemList()); if (moveto) { sp_selection_change_layer_maintain_clones(items,moveto); - GSList *temp_clip = NULL; - sp_selection_copy_impl(items, &temp_clip, dt->doc()->getReprDoc()); // we're in the same doc, so no need to copy defs + std::vector<Inkscape::XML::Node*> temp_clip; + sp_selection_copy_impl(items, temp_clip, dt->doc()->getReprDoc()); // we're in the same doc, so no need to copy defs sp_selection_delete_impl(items, false, false); - GSList *copied = sp_selection_paste_impl(dt->getDocument(), moveto, &temp_clip); - selection->setReprList((GSList const *) copied); - g_slist_free(copied); - if (temp_clip) g_slist_free(temp_clip); + std::vector<Inkscape::XML::Node*> copied = sp_selection_paste_impl(dt->getDocument(), moveto, temp_clip); + selection->setReprList(copied); + if (!temp_clip.empty()) temp_clip.clear(); if (moveto) dt->setCurrentLayer(moveto); if ( !suppressDone ) { DocumentUndo::done(dt->getDocument(), SP_VERB_LAYER_MOVE_TO, _("Move selection to layer")); } } - - g_slist_free(const_cast<GSList *>(items)); } static bool @@ -1495,8 +1471,9 @@ static bool selection_contains_both_clone_and_original(Inkscape::Selection *selection) { bool clone_with_original = false; - for (GSList const *l = selection->itemList(); l != NULL; l = l->next) { - SPItem *item = dynamic_cast<SPItem *>(static_cast<SPObject *>(l->data)); + std::vector<SPItem*> items = selection->itemList(); + for (std::vector<SPItem*>::const_iterator l=items.begin();l!=items.end() ;l++) { + SPItem *item = *l; if (item) { clone_with_original |= selection_contains_original(item, selection); if (clone_with_original) @@ -1539,9 +1516,9 @@ void sp_selection_apply_affine(Inkscape::Selection *selection, Geom::Affine cons persp3d_apply_affine_transformation(transf_persp, affine); } - - for (GSList const *l = selection->itemList(); l != NULL; l = l->next) { - SPItem *item = dynamic_cast<SPItem *>(static_cast<SPObject *>(l->data)); + std::vector<SPItem*> items = selection->itemList(); + for (std::vector<SPItem*>::const_iterator l=items.begin();l!=items.end() ;l++) { + SPItem *item = *l; if( dynamic_cast<SPRoot *>(item) ) { // An SVG element cannot have a transform. We could change 'x' and 'y' in response @@ -1710,10 +1687,9 @@ void sp_selection_remove_transform(SPDesktop *desktop) Inkscape::Selection *selection = desktop->getSelection(); - GSList const *l = const_cast<GSList *>(selection->reprList()); - while (l != NULL) { - ((Inkscape::XML::Node*)l->data)->setAttribute("transform", NULL, false); - l = l->next; + std::vector<Inkscape::XML::Node*> items = selection->reprList(); + for (std::vector<Inkscape::XML::Node*>::const_iterator l=items.begin();l!=items.end() ;l++) { + (*l)->setAttribute("transform", NULL, false); } DocumentUndo::done(desktop->getDocument(), SP_VERB_OBJECT_FLATTEN, @@ -1811,10 +1787,10 @@ void sp_selection_rotate_90(SPDesktop *desktop, bool ccw) if (selection->isEmpty()) return; - GSList const *l = selection->itemList(); + std::vector<SPItem*> items = selection->itemList(); Geom::Rotate const rot_90(Geom::Point(0, ccw ? 1 : -1)); // pos. or neg. rotation, depending on the value of ccw - for (GSList const *l2 = l ; l2 != NULL ; l2 = l2->next) { - SPItem *item = dynamic_cast<SPItem *>(static_cast<SPObject *>(l2->data)); + for (std::vector<SPItem*>::const_iterator l=items.begin();l!=items.end() ;l++) { + SPItem *item = *l; if (item) { sp_item_rotate_rel(item, rot_90); } else { @@ -1870,15 +1846,24 @@ void sp_select_same_fill_stroke_style(SPDesktop *desktop, gboolean fill, gboolea bool onlyvisible = prefs->getBool("/options/kbselection/onlyvisible", true); bool onlysensitive = prefs->getBool("/options/kbselection/onlysensitive", true); bool ingroups = TRUE; - - GSList *all_list = get_all_items(NULL, desktop->currentRoot(), desktop, onlyvisible, onlysensitive, ingroups, NULL); - GSList *all_matches = NULL; + std::vector<SPItem*> x,y; + std::vector<SPItem*> all_list = get_all_items(x, desktop->currentRoot(), desktop, onlyvisible, onlysensitive, ingroups, y); + std::vector<SPItem*> all_matches; Inkscape::Selection *selection = desktop->getSelection(); + std::vector<SPItem*> items = selection->itemList(); + + std::vector<SPItem*> tmp; + for (std::vector<SPItem*>::const_iterator iter=all_list.begin();iter!=all_list.end();iter++) { + if(!SP_IS_GROUP(*iter)){ + tmp.push_back(*iter); + } + } + all_list=tmp; - for (GSList const* sel_iter = selection->itemList(); sel_iter; sel_iter = sel_iter->next) { - SPItem *sel = dynamic_cast<SPItem *>(static_cast<SPObject *>(sel_iter->data)); - GSList *matches = all_list; + for (std::vector<SPItem*>::const_iterator sel_iter=items.begin();sel_iter!=items.end();sel_iter++) { + SPItem *sel = *sel_iter; + std::vector<SPItem*> matches = all_list; if (fill && stroke && style) { matches = sp_get_same_style(sel, matches); } @@ -1891,19 +1876,12 @@ void sp_select_same_fill_stroke_style(SPDesktop *desktop, gboolean fill, gboolea else if (style) { matches = sp_get_same_style(sel, matches,SP_STROKE_STYLE_ALL); } - all_matches = g_slist_concat (all_matches, matches); + all_matches.insert(all_matches.end(), matches.begin(),matches.end()); } selection->clear(); selection->setList(all_matches); - if (all_matches) { - g_slist_free(all_matches); - } - if (all_list) { - g_slist_free(all_list); - } - } @@ -1924,14 +1902,15 @@ void sp_select_same_object_type(SPDesktop *desktop) bool onlyvisible = prefs->getBool("/options/kbselection/onlyvisible", true); bool onlysensitive = prefs->getBool("/options/kbselection/onlysensitive", true); bool ingroups = TRUE; - - GSList *all_list = get_all_items(NULL, desktop->currentRoot(), desktop, onlyvisible, onlysensitive, ingroups, NULL); - GSList *matches = all_list; + std::vector<SPItem*> x,y; + std::vector<SPItem*> all_list = get_all_items(x, desktop->currentRoot(), desktop, onlyvisible, onlysensitive, ingroups, y); + std::vector<SPItem*> matches = all_list; Inkscape::Selection *selection = desktop->getSelection(); - for (GSList const* sel_iter = selection->itemList(); sel_iter; sel_iter = sel_iter->next) { - SPItem *sel = dynamic_cast<SPItem *>(static_cast<SPObject *>(sel_iter->data)); + std::vector<SPItem*> items=selection->itemList(); + for (std::vector<SPItem*>::const_iterator sel_iter=items.begin();sel_iter!=items.end();sel_iter++) { + SPItem *sel = *sel_iter; if (sel) { matches = sp_get_same_object_type(sel, matches); } else { @@ -1942,12 +1921,6 @@ void sp_select_same_object_type(SPDesktop *desktop) selection->clear(); selection->setList(matches); - if (matches) { - g_slist_free(matches); - } - if (all_list) { - g_slist_free(all_list); - } } @@ -1956,15 +1929,15 @@ void sp_select_same_object_type(SPDesktop *desktop) * Find all items in src list that have the same fill or stroke style as sel * Return the list of matching items */ -GSList *sp_get_same_fill_or_stroke_color(SPItem *sel, GSList *src, SPSelectStrokeStyleType type) +std::vector<SPItem*> sp_get_same_fill_or_stroke_color(SPItem *sel, std::vector<SPItem*> &src, SPSelectStrokeStyleType type) { - GSList *matches = NULL; + std::vector<SPItem*> matches ; gboolean match = false; SPIPaint *sel_paint = (type == SP_FILL_COLOR) ? &(sel->style->fill) : &(sel->style->stroke); - for (GSList *i = src; i != NULL; i = i->next) { - SPItem *iter = dynamic_cast<SPItem *>(static_cast<SPObject *>(i->data)); + for (std::vector<SPItem*>::const_reverse_iterator i=src.rbegin();i!=src.rend();i++) { + SPItem *iter = *i; if (iter) { SPIPaint *iter_paint = (type == SP_FILL_COLOR) ? &(iter->style->fill) : &(iter->style->stroke); match = false; @@ -1990,8 +1963,8 @@ GSList *sp_get_same_fill_or_stroke_color(SPItem *sel, GSList *src, SPSelectStrok } } else if (dynamic_cast<SPPattern *>(sel_server) && dynamic_cast<SPPattern *>(iter_server)) { - SPPattern *sel_pat = pattern_getroot(dynamic_cast<SPPattern *>(sel_server)); - SPPattern *iter_pat = pattern_getroot(dynamic_cast<SPPattern *>(iter_server)); + SPPattern *sel_pat = dynamic_cast<SPPattern *>(sel_server)->rootPattern(); + SPPattern *iter_pat = dynamic_cast<SPPattern *>(iter_server)->rootPattern(); if (sel_pat == iter_pat) { match = true; } @@ -2003,7 +1976,7 @@ GSList *sp_get_same_fill_or_stroke_color(SPItem *sel, GSList *src, SPSelectStrok } if (match) { - matches = g_slist_prepend(matches, iter); + matches.push_back(iter); } } else { g_assert_not_reached(); @@ -2054,17 +2027,16 @@ static bool item_type_match (SPItem *i, SPItem *j) * Find all items in src list that have the same object type as sel by type * Return the list of matching items */ -GSList *sp_get_same_object_type(SPItem *sel, GSList *src) +std::vector<SPItem*> sp_get_same_object_type(SPItem *sel, std::vector<SPItem*> &src) { - GSList *matches = NULL; + std::vector<SPItem*> matches; - for (GSList *i = src; i != NULL; i = i->next) { - SPItem *item = dynamic_cast<SPItem *>(static_cast<SPObject *>(i->data)); + for (std::vector<SPItem*>::const_reverse_iterator i=src.rbegin();i!=src.rend();i++) { + SPItem *item = *i; if (item && item_type_match(sel, item) && !item->cloned) { - matches = g_slist_prepend (matches, item); + matches.push_back(item); } } - return matches; } @@ -2074,9 +2046,9 @@ GSList *sp_get_same_fill_or_stroke_color(SPItem *sel, GSList *src, SPSelectStrok * Find all items in src list that have the same stroke style as sel by type * Return the list of matching items */ -GSList *sp_get_same_style(SPItem *sel, GSList *src, SPSelectStrokeStyleType type) +std::vector<SPItem*> sp_get_same_style(SPItem *sel, std::vector<SPItem*> &src, SPSelectStrokeStyleType type) { - GSList *matches = NULL; + std::vector<SPItem*> matches; bool match = false; SPStyle *sel_style = sel->style; @@ -2092,16 +2064,16 @@ GSList *sp_get_same_style(SPItem *sel, GSList *src, SPSelectStrokeStyleType type * Stroke width needs to handle transformations, so call this function * to get the transformed stroke width */ - GSList *objects = NULL; + std::vector<SPItem*> objects; SPStyle *sel_style_for_width = NULL; if (type == SP_STROKE_STYLE_WIDTH || type == SP_STROKE_STYLE_ALL || type==SP_STYLE_ALL ) { - objects = g_slist_prepend(objects, sel); + objects.push_back(sel); sel_style_for_width = new SPStyle(SP_ACTIVE_DOCUMENT); objects_query_strokewidth (objects, sel_style_for_width); } bool match_g; - for (GSList *i = src; i != NULL; i = i->next) { - SPItem *iter = dynamic_cast<SPItem *>(static_cast<SPObject *>(i->data)); + for (std::vector<SPItem*>::const_iterator i=src.begin();i!=src.end();i++) { + SPItem *iter = *i; if (iter) { match_g=true; SPStyle *iter_style = iter->style; @@ -2110,15 +2082,14 @@ GSList *sp_get_same_style(SPItem *sel, GSList *src, SPSelectStrokeStyleType type if (type == SP_STROKE_STYLE_WIDTH|| type == SP_STROKE_STYLE_ALL|| type==SP_STYLE_ALL) { match = (sel_style->stroke_width.set == iter_style->stroke_width.set); if (sel_style->stroke_width.set && iter_style->stroke_width.set) { - GSList *objects = NULL; - objects = g_slist_prepend(objects, iter); + std::vector<SPItem*> objects; + objects.insert(objects.begin(),iter); SPStyle tmp_style(SP_ACTIVE_DOCUMENT); objects_query_strokewidth (objects, &tmp_style); if (sel_style_for_width) { match = (sel_style_for_width->stroke_width.computed == tmp_style.stroke_width.computed); } - g_slist_free(objects); } } match_g = match_g && match; @@ -2141,10 +2112,10 @@ GSList *sp_get_same_style(SPItem *sel, GSList *src, SPSelectStrokeStyleType type } } } - match_g = match_g && match; + match_g = match_g && match; if (match_g) { while (iter->cloned) iter=dynamic_cast<SPItem *>(iter->parent); - matches = g_slist_prepend(matches, iter); + matches.insert(matches.begin(),iter); } } else { g_assert_not_reached(); @@ -2152,8 +2123,6 @@ GSList *sp_get_same_style(SPItem *sel, GSList *src, SPSelectStrokeStyleType type } if( sel_style_for_width != NULL ) delete sel_style_for_width; - g_slist_free(objects); - return matches; } @@ -2307,17 +2276,9 @@ sp_selection_move_screen(Inkscape::Selection *selection, gdouble dx, gdouble dy) } } -namespace { -template <typename D> -SPItem *next_item(SPDesktop *desktop, GSList *path, SPObject *root, - bool only_in_viewport, PrefsSelectionContext inlayer, bool onlyvisible, bool onlysensitive); -template <typename D> -SPItem *next_item_from_list(SPDesktop *desktop, GSList const *items, SPObject *root, - bool only_in_viewport, PrefsSelectionContext inlayer, bool onlyvisible, bool onlysensitive); - -struct Forward { +typedef struct Forward { typedef SPObject *Iterator; static Iterator children(SPObject *o) { return o->firstChild(); } @@ -2326,9 +2287,9 @@ struct Forward { static SPObject *object(Iterator i) { return i; } static Iterator next(Iterator i) { return i->getNext(); } -}; +} Forward; -struct ListReverse { +typedef struct ListReverse { typedef GSList *Iterator; static Iterator children(SPObject *o) { @@ -2359,8 +2320,87 @@ private: } return list; } -}; +} ListReverse; + + + +template <typename D> +SPItem *next_item(SPDesktop *desktop, GSList *path, SPObject *root, + bool only_in_viewport, PrefsSelectionContext inlayer, bool onlyvisible, bool onlysensitive) +{ + typename D::Iterator children; + typename D::Iterator iter; + + SPItem *found=NULL; + if (path) { + SPObject *object=reinterpret_cast<SPObject *>(path->data); + g_assert(object->parent == root); + if (desktop->isLayer(object)) { + found = next_item<D>(desktop, path->next, object, only_in_viewport, inlayer, onlyvisible, onlysensitive); + } + iter = children = D::siblings_after(object); + } else { + iter = children = D::children(root); + } + + while ( iter && !found ) { + SPObject *object=D::object(iter); + if (desktop->isLayer(object)) { + if (PREFS_SELECTION_LAYER != inlayer) { // recurse into sublayers + found = next_item<D>(desktop, NULL, object, only_in_viewport, inlayer, onlyvisible, onlysensitive); + } + } else { + SPItem *item = dynamic_cast<SPItem *>(object); + if ( item && + ( !only_in_viewport || desktop->isWithinViewport(item) ) && + ( !onlyvisible || !desktop->itemIsHidden(item)) && + ( !onlysensitive || !item->isLocked()) && + !desktop->isLayer(item) ) + { + found = item; + } + } + iter = D::next(iter); + } + + D::dispose(children); + + return found; +} + + +template <typename D> +SPItem *next_item_from_list(SPDesktop *desktop, std::vector<SPItem*> const items, + SPObject *root, bool only_in_viewport, PrefsSelectionContext inlayer, bool onlyvisible, bool onlysensitive) +{ + SPObject *current=root; + for(std::vector<SPItem*>::const_iterator i = items.begin();i!=items.end();i++) { + SPItem *item = *i; + if ( root->isAncestorOf(item) && + ( !only_in_viewport || desktop->isWithinViewport(item) ) ) + { + current = item; + break; + } + } + + GSList *path=NULL; + while ( current != root ) { + path = g_slist_prepend(path, current); + current = current->parent; + } + + SPItem *next; + // first, try from the current object + next = next_item<D>(desktop, path, root, only_in_viewport, inlayer, onlyvisible, onlysensitive); + g_slist_free(path); + + if (!next) { // if we ran out of objects, start over at the root + next = next_item<D>(desktop, NULL, root, only_in_viewport, inlayer, onlyvisible, onlysensitive); + } + + return next; } void @@ -2487,88 +2527,8 @@ void sp_selection_edit_clip_or_mask(SPDesktop * /*dt*/, bool /*clip*/) } -namespace { - -template <typename D> -SPItem *next_item_from_list(SPDesktop *desktop, GSList const *items, - SPObject *root, bool only_in_viewport, PrefsSelectionContext inlayer, bool onlyvisible, bool onlysensitive) -{ - SPObject *current=root; - while (items) { - SPItem *item = dynamic_cast<SPItem *>(static_cast<SPObject *>(items->data)); - if ( root->isAncestorOf(item) && - ( !only_in_viewport || desktop->isWithinViewport(item) ) ) - { - current = item; - break; - } - items = items->next; - } - - GSList *path=NULL; - while ( current != root ) { - path = g_slist_prepend(path, current); - current = current->parent; - } - - SPItem *next; - // first, try from the current object - next = next_item<D>(desktop, path, root, only_in_viewport, inlayer, onlyvisible, onlysensitive); - g_slist_free(path); - - if (!next) { // if we ran out of objects, start over at the root - next = next_item<D>(desktop, NULL, root, only_in_viewport, inlayer, onlyvisible, onlysensitive); - } - - return next; -} - -template <typename D> -SPItem *next_item(SPDesktop *desktop, GSList *path, SPObject *root, - bool only_in_viewport, PrefsSelectionContext inlayer, bool onlyvisible, bool onlysensitive) -{ - typename D::Iterator children; - typename D::Iterator iter; - - SPItem *found=NULL; - if (path) { - SPObject *object=reinterpret_cast<SPObject *>(path->data); - g_assert(object->parent == root); - if (desktop->isLayer(object)) { - found = next_item<D>(desktop, path->next, object, only_in_viewport, inlayer, onlyvisible, onlysensitive); - } - iter = children = D::siblings_after(object); - } else { - iter = children = D::children(root); - } - while ( iter && !found ) { - SPObject *object=D::object(iter); - if (desktop->isLayer(object)) { - if (PREFS_SELECTION_LAYER != inlayer) { // recurse into sublayers - found = next_item<D>(desktop, NULL, object, only_in_viewport, inlayer, onlyvisible, onlysensitive); - } - } else { - SPItem *item = dynamic_cast<SPItem *>(object); - if ( item && - ( !only_in_viewport || desktop->isWithinViewport(item) ) && - ( !onlyvisible || !desktop->itemIsHidden(item)) && - ( !onlysensitive || !item->isLocked()) && - !desktop->isLayer(item) ) - { - found = item; - } - } - iter = D::next(iter); - } - - D::dispose(children); - - return found; -} - -} /** * If \a item is not entirely visible then adjust visible area to centre on the centre on of @@ -2608,17 +2568,17 @@ void sp_selection_clone(SPDesktop *desktop) return; } - GSList *reprs = g_slist_copy(const_cast<GSList *>(selection->reprList())); + std::vector<Inkscape::XML::Node*> reprs (selection->reprList()); selection->clear(); // sorting items from different parents sorts each parent's subset without possibly mixing them, just what we need - reprs = g_slist_sort(reprs, (GCompareFunc) sp_repr_compare_position); + sort(reprs.begin(),reprs.end(),sp_repr_compare_position_bool); - GSList *newsel = NULL; + std::vector<Inkscape::XML::Node*> newsel; - while (reprs) { - Inkscape::XML::Node *sel_repr = static_cast<Inkscape::XML::Node *>(reprs->data); + for(std::vector<Inkscape::XML::Node*>::const_iterator i=reprs.begin();i!=reprs.end();i++){ + Inkscape::XML::Node *sel_repr = *i; Inkscape::XML::Node *parent = sel_repr->parent(); Inkscape::XML::Node *clone = xml_doc->createElement("svg:use"); @@ -2634,8 +2594,7 @@ void sp_selection_clone(SPDesktop *desktop) // add the new clone to the top of the original's parent parent->appendChild(clone); - newsel = g_slist_prepend(newsel, clone); - reprs = g_slist_remove(reprs, sel_repr); + newsel.push_back(clone); Inkscape::GC::release(clone); } @@ -2643,8 +2602,6 @@ void sp_selection_clone(SPDesktop *desktop) C_("Action", "Clone")); selection->setReprList(newsel); - - g_slist_free(newsel); } void @@ -2670,11 +2627,9 @@ sp_selection_relink(SPDesktop *desktop) // Get a copy of current selection. bool relinked = false; - for (GSList *items = const_cast<GSList *>(selection->itemList()); - items != NULL; - items = items->next) - { - SPItem *item = static_cast<SPItem *>(items->data); + std::vector<SPItem*> items=selection->itemList(); + for (std::vector<SPItem*>::const_iterator i=items.begin();i!=items.end();i++){ + SPItem *item = *i; if (dynamic_cast<SPUse *>(item)) { item->getRepr()->setAttribute("xlink:href", newref); @@ -2708,13 +2663,11 @@ sp_selection_unlink(SPDesktop *desktop) } // Get a copy of current selection. - GSList *new_select = NULL; + std::vector<SPItem*> new_select; bool unlinked = false; - for (GSList *items = g_slist_copy(const_cast<GSList *>(selection->itemList())); - items != NULL; - items = items->next) - { - SPItem *item = static_cast<SPItem *>(items->data); + std::vector<SPItem*> items=selection->itemList(); + for (std::vector<SPItem*>::const_reverse_iterator i=items.rbegin();i!=items.rend();i++){ + SPItem *item = *i; if (dynamic_cast<SPText *>(item)) { SPObject *tspan = sp_tref_convert_to_tspan(item); @@ -2730,7 +2683,7 @@ sp_selection_unlink(SPDesktop *desktop) if (!(dynamic_cast<SPUse *>(item) || dynamic_cast<SPTRef *>(item))) { // keep the non-use item in the new selection - new_select = g_slist_prepend(new_select, item); + new_select.push_back(item); continue; } @@ -2740,7 +2693,7 @@ sp_selection_unlink(SPDesktop *desktop) unlink = use->unlink(); // Unable to unlink use (external or invalid href?) if (!unlink) { - new_select = g_slist_prepend(new_select, item); + new_select.push_back(item); continue; } } else /*if (SP_IS_TREF(use))*/ { @@ -2750,13 +2703,12 @@ sp_selection_unlink(SPDesktop *desktop) unlinked = true; // Add ungrouped items to the new selection. - new_select = g_slist_prepend(new_select, unlink); + new_select.push_back(unlink); } - if (new_select) { // set new selection + if (!new_select.empty()) { // set new selection selection->clear(); selection->setList(new_select); - g_slist_free(new_select); } if (!unlinked) { desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("<b>No clones to unlink</b> in the selection.")); @@ -2779,7 +2731,9 @@ sp_select_clone_original(SPDesktop *desktop) gchar const *error = _("Select a <b>clone</b> to go to its original. Select a <b>linked offset</b> to go to its source. Select a <b>text on path</b> to go to the path. Select a <b>flowed text</b> to go to its frame."); // Check if other than two objects are selected - if (g_slist_length(const_cast<GSList *>(selection->itemList())) != 1 || !item) { + + std::vector<SPItem*> items=selection->itemList(); + if (items.size() != 1 || !item) { desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, error); return; } @@ -2876,14 +2830,15 @@ void sp_selection_clone_original_path_lpe(SPDesktop *desktop) Inkscape::SVGOStringStream os; SPObject * firstItem = NULL; - for (const GSList * item = selection->itemList(); item != NULL; item = item->next) { - if (SP_IS_SHAPE(item->data) || SP_IS_TEXT(item->data)) { + std::vector<SPItem*> items=selection->itemList(); + for (std::vector<SPItem*>::const_iterator i=items.begin();i!=items.end();i++){ + if (SP_IS_SHAPE(*i) || SP_IS_TEXT(*i)) { if (firstItem) { os << "|"; } else { - firstItem = SP_ITEM(item->data); + firstItem = SP_ITEM(*i); } - os << '#' << SP_ITEM(item->data)->getId() << ",0"; + os << '#' << SP_ITEM(*i)->getId() << ",0"; } } if (firstItem) { @@ -2960,12 +2915,12 @@ void sp_selection_to_marker(SPDesktop *desktop, bool apply) Geom::Point center( *c - corner ); // As defined by rotation center center[Geom::Y] = -center[Geom::Y]; - GSList *items = g_slist_copy(const_cast<GSList *>(selection->itemList())); + std::vector<SPItem*> items(selection->itemList()); //items = g_slist_sort(items, (GCompareFunc) sp_object_compare_position); // Why needed? // bottommost object, after sorting - SPObject *parent = SP_OBJECT(items->data)->parent; + SPObject *parent = items[0]->parent; Geom::Affine parent_transform; { @@ -2978,10 +2933,10 @@ void sp_selection_to_marker(SPDesktop *desktop, bool apply) } // Create a list of duplicates, to be pasted inside marker element. - GSList *repr_copies = NULL; - for (GSList *i = items; i != NULL; i = i->next) { - Inkscape::XML::Node *dup = SP_OBJECT(i->data)->getRepr()->duplicate(xml_doc); - repr_copies = g_slist_prepend(repr_copies, dup); + std::vector<Inkscape::XML::Node*> repr_copies; + for (std::vector<SPItem*>::const_reverse_iterator i=items.rbegin();i!=items.rend();i++){ + Inkscape::XML::Node *dup = (*i)->getRepr()->duplicate(xml_doc); + repr_copies.push_back(dup); } Geom::Rect bbox(desktop->dt2doc(r->min()), desktop->dt2doc(r->max())); @@ -2989,8 +2944,8 @@ void sp_selection_to_marker(SPDesktop *desktop, bool apply) if (apply) { // Delete objects so that their clones don't get alerted; // the objects will be restored inside the marker element. - for (GSList *i = items; i != NULL; i = i->next) { - SPObject *item = reinterpret_cast<SPObject*>(i->data); + for (std::vector<SPItem*>::const_iterator i=items.begin();i!=items.end();i++){ + SPObject *item = *i; item->deleteObject(false); } } @@ -3009,7 +2964,6 @@ void sp_selection_to_marker(SPDesktop *desktop, bool apply) prefs->setInt("/options/clonecompensation/value", saved_compensation); - g_slist_free(items); DocumentUndo::done(doc, SP_VERB_EDIT_SELECTION_2_MARKER, _("Objects to marker")); @@ -3018,8 +2972,9 @@ void sp_selection_to_marker(SPDesktop *desktop, bool apply) static void sp_selection_to_guides_recursive(SPItem *item, bool wholegroups) { SPGroup *group = dynamic_cast<SPGroup *>(item); if (group && !dynamic_cast<SPBox3D *>(item) && !wholegroups) { - for (GSList *i = sp_item_group_item_list(group); i != NULL; i = i->next) { - sp_selection_to_guides_recursive(static_cast<SPItem*>(i->data), wholegroups); + std::vector<SPItem*> items=sp_item_group_item_list(group); + for (std::vector<SPItem*>::const_iterator i=items.begin();i!=items.end();i++){ + sp_selection_to_guides_recursive(*i, wholegroups); } } else { item->convert_to_guides(); @@ -3034,9 +2989,9 @@ void sp_selection_to_guides(SPDesktop *desktop) SPDocument *doc = desktop->getDocument(); Inkscape::Selection *selection = desktop->getSelection(); // we need to copy the list because it gets reset when objects are deleted - GSList *items = g_slist_copy(const_cast<GSList *>(selection->itemList())); + std::vector<SPItem*> items(selection->itemList()); - if (!items) { + if (items.empty()) { desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select <b>object(s)</b> to convert to guides.")); return; } @@ -3049,8 +3004,8 @@ void sp_selection_to_guides(SPDesktop *desktop) // and its entry in the selection list is invalid (crash). // Therefore: first convert all, then delete all. - for (GSList const *i = items; i != NULL; i = i->next) { - sp_selection_to_guides_recursive(static_cast<SPItem*>(i->data), wholegroups); + for (std::vector<SPItem*>::const_iterator i=items.begin();i!=items.end();i++){ + sp_selection_to_guides_recursive(*i, wholegroups); } if (deleteitems) { @@ -3058,8 +3013,6 @@ void sp_selection_to_guides(SPDesktop *desktop) sp_selection_delete_impl(items); } - g_slist_free(items); - DocumentUndo::done(doc, SP_VERB_EDIT_SELECTION_2_GUIDES, _("Objects to guides")); } @@ -3102,18 +3055,18 @@ void sp_selection_symbol(SPDesktop *desktop, bool /*apply*/ ) doc->ensureUpToDate(); - GSList *items = g_slist_copy(const_cast<GSList *>(selection->list())); + std::vector<SPObject*> items(selection->list()); // Keep track of parent, this is where <use> will be inserted. - Inkscape::XML::Node *the_first_repr = reinterpret_cast<SPObject *>( items->data )->getRepr(); + Inkscape::XML::Node *the_first_repr = items[0]->getRepr(); Inkscape::XML::Node *the_parent_repr = the_first_repr->parent(); // Find out if we have a single group bool single_group = false; SPGroup *the_group = NULL; Geom::Affine transform; - if( g_slist_length( items ) == 1 ) { - SPObject *object = reinterpret_cast<SPObject *>( items->data ); + if( items.size() == 1 ) { + SPObject *object = items[0]; the_group = dynamic_cast<SPGroup *>(object); if ( the_group ) { single_group = true; @@ -3124,7 +3077,6 @@ void sp_selection_symbol(SPDesktop *desktop, bool /*apply*/ ) if( transform.isTranslation() ) { // Create new list from group children. - g_slist_free(items); items = object->childList(false); // Hack: Temporarily set clone compensation to unmoved, so that we can move clone-originals @@ -3170,8 +3122,8 @@ void sp_selection_symbol(SPDesktop *desktop, bool /*apply*/ ) } // Move selected items to new <symbol> - for (GSList *i = items; i != NULL; i = i->next) { - Inkscape::XML::Node *repr = SP_OBJECT(i->data)->getRepr(); + for (std::vector<SPObject*>::const_iterator i=items.begin();i!=items.end();i++){ + Inkscape::XML::Node *repr = (*i)->getRepr(); repr->parent()->removeChild(repr); symbol_repr->addChild(repr,NULL); } @@ -3197,7 +3149,6 @@ void sp_selection_symbol(SPDesktop *desktop, bool /*apply*/ ) // Clean up Inkscape::GC::release(symbol_repr); - g_slist_free(items); DocumentUndo::done(doc, SP_VERB_EDIT_SYMBOL, _("Group to symbol")); } @@ -3238,26 +3189,25 @@ void sp_selection_unsymbol(SPDesktop *desktop) desktop->currentLayer()->getRepr()->appendChild(group); // Move all children of symbol to group - GSList* children = symbol->childList(false); + std::vector<SPObject*> children = symbol->childList(false); // Converting a group to a symbol inserts a group for non-translational transform. // In converting a symbol back to a group we strip out the inserted group (or any other // group that only adds a transform to the symbol content). - if( g_slist_length( children ) == 1 ) { - SPObject *object = reinterpret_cast<SPObject *>( children->data ); + if( children.size() == 1 ) { + SPObject *object = children[0]; if ( dynamic_cast<SPGroup *>( object ) ) { if( object->getAttribute("style") == NULL || object->getAttribute("class") == NULL ) { group->setAttribute("transform", object->getAttribute("transform")); - g_slist_free(children); children = object->childList(false); } } } - for (GSList* i = children; i != NULL; i = i->next ) { - Inkscape::XML::Node *repr = SP_OBJECT(i->data)->getRepr(); + for (std::vector<SPObject*>::const_reverse_iterator i=children.rbegin();i!=children.rend();i++){ + Inkscape::XML::Node *repr = (*i)->getRepr(); repr->parent()->removeChild(repr); group->addChild(repr,NULL); } @@ -3283,7 +3233,6 @@ void sp_selection_unsymbol(SPDesktop *desktop) // Clean up Inkscape::GC::release(group); - g_slist_free(children); DocumentUndo::done(doc, SP_VERB_EDIT_UNSYMBOL, _("Group from symbol")); } @@ -3318,12 +3267,12 @@ sp_selection_tile(SPDesktop *desktop, bool apply) move_p[Geom::Y] = -move_p[Geom::Y]; Geom::Affine move = Geom::Affine(Geom::Translate(move_p)); - GSList *items = g_slist_copy(const_cast<GSList *>(selection->itemList())); + std::vector<SPItem*> items (selection->itemList()); - items = g_slist_sort(items, (GCompareFunc) sp_object_compare_position); + sort(items.begin(),items.end(),sp_object_compare_position_bool); // bottommost object, after sorting - SPObject *parent = SP_OBJECT(items->data)->parent; + SPObject *parent = items[0]->parent; Geom::Affine parent_transform; @@ -3337,23 +3286,21 @@ sp_selection_tile(SPDesktop *desktop, bool apply) } // remember the position of the first item - gint pos = SP_OBJECT(items->data)->getRepr()->position(); + gint pos = items[0]->getRepr()->position(); // create a list of duplicates - GSList *repr_copies = NULL; - for (GSList *i = items; i != NULL; i = i->next) { - Inkscape::XML::Node *dup = SP_OBJECT(i->data)->getRepr()->duplicate(xml_doc); - repr_copies = g_slist_prepend(repr_copies, dup); + std::vector<Inkscape::XML::Node*> repr_copies; + for (std::vector<SPItem*>::const_iterator i=items.begin();i!=items.end();i++){ + Inkscape::XML::Node *dup = (*i)->getRepr()->duplicate(xml_doc); + repr_copies.push_back(dup); } - // restore the z-order after prepends - repr_copies = g_slist_reverse(repr_copies); Geom::Rect bbox(desktop->dt2doc(r->min()), desktop->dt2doc(r->max())); if (apply) { // delete objects so that their clones don't get alerted; this object will be restored shortly - for (GSList *i = items; i != NULL; i = i->next) { - SPObject *item = reinterpret_cast<SPObject*>(i->data); + for (std::vector<SPItem*>::const_iterator i=items.begin();i!=items.end();i++){ + SPObject *item = *i; item->deleteObject(false); } } @@ -3365,7 +3312,7 @@ sp_selection_tile(SPDesktop *desktop, bool apply) int saved_compensation = prefs->getInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED); prefs->setInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED); - gchar const *pat_id = pattern_tile(repr_copies, bbox, doc, + gchar const *pat_id = SPPattern::produce(repr_copies, bbox, doc, ( Geom::Affine(Geom::Translate(desktop->dt2doc(Geom::Point(r->min()[Geom::X], r->max()[Geom::Y])))) * parent_transform.inverse() ), @@ -3399,7 +3346,6 @@ sp_selection_tile(SPDesktop *desktop, bool apply) selection->set(rectangle); } - g_slist_free(items); DocumentUndo::done(doc, SP_VERB_EDIT_TILE, _("Objects to pattern")); @@ -3422,15 +3368,13 @@ void sp_selection_untile(SPDesktop *desktop) return; } - GSList *new_select = NULL; + std::vector<SPItem*> new_select; bool did = false; - for (GSList *items = g_slist_copy(const_cast<GSList *>(selection->itemList())); - items != NULL; - items = items->next) { - - SPItem *item = static_cast<SPItem *>(items->data); + std::vector<SPItem*> items(selection->itemList()); + for (std::vector<SPItem*>::const_reverse_iterator i=items.rbegin();i!=items.rend();i++){ + SPItem *item = *i; SPStyle *style = item->style; @@ -3446,9 +3390,9 @@ void sp_selection_untile(SPDesktop *desktop) did = true; - SPPattern *pattern = pattern_getroot(basePat); + SPPattern *pattern = basePat->rootPattern(); - Geom::Affine pat_transform = pattern_patternTransform(basePat); + Geom::Affine pat_transform = basePat->getTransform(); pat_transform *= item->transform; for (SPObject *child = pattern->firstChild() ; child != NULL; child = child->next ) { @@ -3466,7 +3410,7 @@ void sp_selection_untile(SPDesktop *desktop) Geom::Affine transform( i->transform * pat_transform ); i->doWriteTransform(i->getRepr(), transform); - new_select = g_slist_prepend(new_select, i); + new_select.push_back(i); } else { g_assert_not_reached(); } @@ -3493,18 +3437,14 @@ void sp_selection_get_export_hints(Inkscape::Selection *selection, Glib::ustring return; } - GSList const *reprlst = selection->reprList(); + std::vector<Inkscape::XML::Node*> const reprlst = selection->reprList(); bool filename_search = TRUE; bool xdpi_search = TRUE; bool ydpi_search = TRUE; - for (; reprlst != NULL && - filename_search && - xdpi_search && - ydpi_search; - reprlst = reprlst->next) { + for (std::vector<Inkscape::XML::Node*>::const_iterator i=reprlst.begin();filename_search&&xdpi_search&&ydpi_search&&i!=reprlst.end();i++){ gchar const *dpi_string; - Inkscape::XML::Node * repr = static_cast<Inkscape::XML::Node *>(reprlst->data); + Inkscape::XML::Node *repr = *i; if (filename_search) { const gchar* tmp = repr->attribute("inkscape:export-filename"); @@ -3590,10 +3530,10 @@ void sp_selection_create_bitmap_copy(SPDesktop *desktop) } // List of the items to show; all others will be hidden - GSList *items = g_slist_copy(const_cast<GSList *>(selection->itemList())); + std::vector<SPItem*> items(selection->itemList()); // Sort items so that the topmost comes last - items = g_slist_sort(items, (GCompareFunc) sp_item_repr_compare_position); + sort(items.begin(),items.end(),sp_item_repr_compare_position_bool); // Generate a random value from the current time (you may create bitmap from the same object(s) // multiple times, and this is done so that they don't clash) @@ -3604,7 +3544,7 @@ void sp_selection_create_bitmap_copy(SPDesktop *desktop) // Create the filename. gchar *const basename = g_strdup_printf("%s-%s-%u.png", document->getName(), - SP_OBJECT(items->data)->getRepr()->attribute("id"), + items[0]->getRepr()->attribute("id"), current); // Imagemagick is known not to handle spaces in filenames, so we replace anything but letters, // digits, and a few other chars, with "_" @@ -3624,8 +3564,8 @@ void sp_selection_create_bitmap_copy(SPDesktop *desktop) //g_print("%s\n", filepath); // Remember parent and z-order of the topmost one - gint pos = SP_OBJECT(g_slist_last(items)->data)->getRepr()->position(); - SPObject *parent_object = SP_OBJECT(g_slist_last(items)->data)->parent; + gint pos = items.back()->getRepr()->position(); + SPObject *parent_object = items.back()->parent; Inkscape::XML::Node *parent = parent_object->getRepr(); // Calculate resolution @@ -3717,8 +3657,6 @@ void sp_selection_create_bitmap_copy(SPDesktop *desktop) true, /*bool force_overwrite,*/ items); - g_slist_free(items); - // Run filter, if any if (run) { g_print("Running external filter: %s\n", run); @@ -3793,22 +3731,20 @@ void sp_selection_set_clipgroup(SPDesktop *desktop) return; } - GSList const *l = const_cast<GSList *>(selection->reprList()); - - GSList *p = g_slist_copy(const_cast<GSList *>(l)); + std::vector<Inkscape::XML::Node*> p(selection->reprList()); - p = g_slist_sort(p, (GCompareFunc) sp_repr_compare_position); + sort(p.begin(),p.end(),sp_repr_compare_position_bool); selection->clear(); - gint topmost = (static_cast<Inkscape::XML::Node *>(g_slist_last(p)->data))->position(); - Inkscape::XML::Node *topmost_parent = (static_cast<Inkscape::XML::Node *>(g_slist_last(p)->data))->parent(); + int topmost = (p.back())->position(); + Inkscape::XML::Node *topmost_parent = (p.back())->parent(); Inkscape::XML::Node *inner = xml_doc->createElement("svg:g"); inner->setAttribute("inkscape:label", "Clip"); - while (p) { - Inkscape::XML::Node *current = static_cast<Inkscape::XML::Node *>(p->data); + for(std::vector<Inkscape::XML::Node*>::const_iterator i=p.begin();i!=p.end();i++){ + Inkscape::XML::Node *current = *i; if (current->parent() == topmost_parent) { Inkscape::XML::Node *spnew = current->duplicate(xml_doc); @@ -3817,7 +3753,7 @@ void sp_selection_set_clipgroup(SPDesktop *desktop) Inkscape::GC::release(spnew); topmost --; // only reduce count for those items deleted from topmost_parent } else { // move it to topmost_parent first - GSList *temp_clip = NULL; + std::vector<Inkscape::XML::Node*> temp_clip; // At this point, current may already have no item, due to its being a clone whose original is already moved away // So we copy it artificially calculating the transform from its repr->attr("transform") and the parent transform @@ -3833,15 +3769,14 @@ void sp_selection_set_clipgroup(SPDesktop *desktop) // then, if this is clone, looking up its original in that array and pre-multiplying // it by the inverse of that original's transform diff. - sp_selection_copy_one(current, item_t, &temp_clip, xml_doc); + sp_selection_copy_one(current, item_t, temp_clip, xml_doc); sp_repr_unparent(current); // paste into topmost_parent (temporarily) - GSList *copied = sp_selection_paste_impl(doc, doc->getObjectByRepr(topmost_parent), &temp_clip); - if (temp_clip) g_slist_free(temp_clip); - if (copied) { // if success, + std::vector<Inkscape::XML::Node*> copied = sp_selection_paste_impl(doc, doc->getObjectByRepr(topmost_parent), temp_clip); + if (!copied.empty()) { // if success, // take pasted object (now in topmost_parent) - Inkscape::XML::Node *in_topmost = static_cast<Inkscape::XML::Node *>(copied->data); + Inkscape::XML::Node *in_topmost = copied.back(); // make a copy Inkscape::XML::Node *spnew = in_topmost->duplicate(xml_doc); // remove pasted @@ -3849,10 +3784,8 @@ void sp_selection_set_clipgroup(SPDesktop *desktop) // put its copy into group inner->appendChild(spnew); Inkscape::GC::release(spnew); - g_slist_free(copied); } } - p = g_slist_remove(p, current); } Inkscape::XML::Node *outer = xml_doc->createElement("svg:g"); @@ -3869,13 +3802,11 @@ void sp_selection_set_clipgroup(SPDesktop *desktop) clone->setAttribute("inkscape:transform-center-y", inner->attribute("inkscape:transform-center-y"), false); const Geom::Affine maskTransform(Geom::Affine::identity()); - GSList *templist = NULL; - - templist = g_slist_append(templist, clone); + std::vector<Inkscape::XML::Node*> templist; + templist.push_back(clone); // add the new clone to the top of the original's parent gchar const *mask_id = SPClipPath::create(templist, doc, &maskTransform); - g_slist_free(templist); outer->setAttribute("clip-path", g_strdup_printf("url(#%s)", mask_id)); @@ -3910,7 +3841,7 @@ void sp_selection_set_mask(SPDesktop *desktop, bool apply_clip_path, bool apply_ if ( apply_to_layer && is_empty) { desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select <b>object(s)</b> to create clippath or mask from.")); return; - } else if (!apply_to_layer && ( is_empty || NULL == selection->itemList()->next )) { + } else if (!apply_to_layer && ( is_empty || selection->itemList().size()==1 )) { desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select mask object and <b>object(s)</b> to apply clippath or mask to.")); return; } @@ -3925,18 +3856,18 @@ void sp_selection_set_mask(SPDesktop *desktop, bool apply_clip_path, bool apply_ doc->ensureUpToDate(); - GSList *items = g_slist_copy(const_cast<GSList *>(selection->itemList())); + std::vector<SPItem*> items(selection->itemList()); - items = g_slist_sort(items, (GCompareFunc) sp_object_compare_position); + sort(items.begin(),items.end(),sp_object_compare_position_bool); // See lp bug #542004 selection->clear(); // create a list of duplicates - GSList *mask_items = NULL; - GSList *apply_to_items = NULL; - GSList *items_to_delete = NULL; - GSList *items_to_select = NULL; + std::vector<Inkscape::XML::Node*> mask_items; + std::vector<SPItem*> apply_to_items; + std::vector<SPItem*> items_to_delete; + std::vector<SPItem*> items_to_select; Inkscape::Preferences *prefs = Inkscape::Preferences::get(); bool topmost = prefs->getBool("/options/maskobject/topmost", true); @@ -3945,55 +3876,36 @@ void sp_selection_set_mask(SPDesktop *desktop, bool apply_clip_path, bool apply_ if (apply_to_layer) { // all selected items are used for mask, which is applied to a layer - apply_to_items = g_slist_prepend(apply_to_items, desktop->currentLayer()); + apply_to_items.push_back(SP_ITEM(desktop->currentLayer())); + } + + for (std::vector<SPItem*>::const_iterator i=items.begin();i!=items.end();i++) { + if((!topmost && !apply_to_layer && *i == items.front()) + || (topmost && !apply_to_layer && *i == items.back()) + || apply_to_layer){ - for (GSList *i = items; i != NULL; i = i->next) { - Inkscape::XML::Node *dup = SP_OBJECT(i->data)->getRepr()->duplicate(xml_doc); - mask_items = g_slist_prepend(mask_items, dup); + Geom::Affine oldtr=(*i)->transform; + (*i)->doWriteTransform((*i)->getRepr(), (*i)->i2doc_affine()); + Inkscape::XML::Node *dup = (*i)->getRepr()->duplicate(xml_doc); + (*i)->doWriteTransform((*i)->getRepr(), oldtr); + mask_items.push_back(dup); - SPObject *item = reinterpret_cast<SPObject*>(i->data); if (remove_original) { - items_to_delete = g_slist_prepend(items_to_delete, item); + items_to_delete.push_back(*i); } else { - items_to_select = g_slist_prepend(items_to_select, item); + items_to_select.push_back(*i); } - } - } else if (!topmost) { - // topmost item is used as a mask, which is applied to other items in a selection - GSList *i = items; - Inkscape::XML::Node *dup = SP_OBJECT(i->data)->getRepr()->duplicate(xml_doc); - mask_items = g_slist_prepend(mask_items, dup); - - if (remove_original) { - SPObject *item = reinterpret_cast<SPObject*>(i->data); - items_to_delete = g_slist_prepend(items_to_delete, item); - } - - for (i = i->next; i != NULL; i = i->next) { - apply_to_items = g_slist_prepend(apply_to_items, i->data); - items_to_select = g_slist_prepend(items_to_select, i->data); - } - } else { - GSList *i = NULL; - for (i = items; NULL != i->next; i = i->next) { - apply_to_items = g_slist_prepend(apply_to_items, i->data); - items_to_select = g_slist_prepend(items_to_select, i->data); - } - - Inkscape::XML::Node *dup = SP_OBJECT(i->data)->getRepr()->duplicate(xml_doc); - mask_items = g_slist_prepend(mask_items, dup); - - if (remove_original) { - SPObject *item = reinterpret_cast<SPObject*>(i->data); - items_to_delete = g_slist_prepend(items_to_delete, item); + continue; + }else{ + apply_to_items.push_back(*i); + items_to_select.push_back(*i); } } - g_slist_free(items); - items = NULL; + items.clear(); - if (apply_to_items && grouping == PREFS_MASKOBJECT_GROUPING_ALL) { + if (grouping == PREFS_MASKOBJECT_GROUPING_ALL) { // group all those objects into one group // and apply mask to that Inkscape::XML::Node *group = xml_doc->createElement("svg:g"); @@ -4001,52 +3913,36 @@ void sp_selection_set_mask(SPDesktop *desktop, bool apply_clip_path, bool apply_ // make a note we should ungroup this when unsetting mask group->setAttribute("inkscape:groupmode", "maskhelper"); - GSList *reprs_to_group = NULL; - - for (GSList *i = apply_to_items ; NULL != i ; i = i->next) { - reprs_to_group = g_slist_prepend(reprs_to_group, SP_OBJECT(i->data)->getRepr()); - items_to_select = g_slist_remove(items_to_select, i->data); + std::vector<Inkscape::XML::Node*> reprs_to_group; + for (std::vector<SPItem*>::const_iterator i = apply_to_items.begin(); i != apply_to_items.end(); i++) { + reprs_to_group.push_back(static_cast<SPObject*>(*i)->getRepr()); } - reprs_to_group = g_slist_reverse(reprs_to_group); + items_to_select.clear(); sp_selection_group_impl(reprs_to_group, group, xml_doc, doc); - reprs_to_group = NULL; - // apply clip/mask only to newly created group - g_slist_free(apply_to_items); - apply_to_items = NULL; - apply_to_items = g_slist_prepend(apply_to_items, doc->getObjectByRepr(group)); + apply_to_items.clear(); + apply_to_items.push_back(dynamic_cast<SPItem*>(doc->getObjectByRepr(group))); - items_to_select = g_slist_prepend(items_to_select, doc->getObjectByRepr(group)); + items_to_select.push_back((SPItem*)(doc->getObjectByRepr(group))); Inkscape::GC::release(group); } + if (grouping == PREFS_MASKOBJECT_GROUPING_SEPARATE) { + items_to_select.clear(); + } + gchar const *attributeName = apply_clip_path ? "clip-path" : "mask"; - for (GSList *i = apply_to_items; NULL != i; i = i->next) { - SPItem *item = reinterpret_cast<SPItem *>(i->data); + for (std::vector<SPItem*>::const_reverse_iterator i = apply_to_items.rbegin(); i != apply_to_items.rend(); i++) { + SPItem *item = reinterpret_cast<SPItem *>(*i); // inverted object transform should be applied to a mask object, // as mask is calculated in user space (after applying transform) - Geom::Affine maskTransform(item->transform.inverse()); - - GSList *mask_items_dup = NULL; - for (GSList *mask_item = mask_items; NULL != mask_item; mask_item = mask_item->next) { - Inkscape::XML::Node *dup = reinterpret_cast<Inkscape::XML::Node *>(mask_item->data)->duplicate(xml_doc); - mask_items_dup = g_slist_prepend(mask_items_dup, dup); - } - - gchar const *mask_id = NULL; - if (apply_clip_path) { - mask_id = SPClipPath::create(mask_items_dup, doc, &maskTransform); - } else { - mask_id = sp_mask_create(mask_items_dup, doc, &maskTransform); - } - - g_slist_free(mask_items_dup); - mask_items_dup = NULL; - - Inkscape::XML::Node *current = SP_OBJECT(i->data)->getRepr(); + std::vector<Inkscape::XML::Node*> mask_items_dup; + for(std::vector<Inkscape::XML::Node*>::const_iterator it=mask_items.begin();it!=mask_items.end();it++) + mask_items_dup.push_back((*it)->duplicate(xml_doc)); + Inkscape::XML::Node *current = SP_OBJECT(*i)->getRepr(); // Node to apply mask to Inkscape::XML::Node *apply_mask_to = current; @@ -4058,7 +3954,6 @@ void sp_selection_set_mask(SPDesktop *desktop, bool apply_clip_path, bool apply_ Inkscape::XML::Node *spnew = current->duplicate(xml_doc); gint position = current->position(); - items_to_select = g_slist_remove(items_to_select, item); current->parent()->appendChild(group); sp_repr_unparent(current); group->appendChild(spnew); @@ -4067,29 +3962,31 @@ void sp_selection_set_mask(SPDesktop *desktop, bool apply_clip_path, bool apply_ // Apply clip/mask to group instead apply_mask_to = group; - items_to_select = g_slist_prepend(items_to_select, doc->getObjectByRepr(group)); + items_to_select.push_back(item = (SPItem*)(doc->getObjectByRepr(group))); Inkscape::GC::release(spnew); Inkscape::GC::release(group); } + Geom::Affine maskTransform(item->i2doc_affine().inverse()); + + gchar const *mask_id = NULL; + if (apply_clip_path) { + mask_id = SPClipPath::create(mask_items_dup, doc, &maskTransform); + } else { + mask_id = sp_mask_create(mask_items_dup, doc, &maskTransform); + } + apply_mask_to->setAttribute(attributeName, Glib::ustring("url(#") + mask_id + ')'); } - g_slist_free(mask_items); - g_slist_free(apply_to_items); - - for (GSList *i = items_to_delete; NULL != i; i = i->next) { - SPObject *item = reinterpret_cast<SPObject*>(i->data); + for (std::vector<SPItem*>::const_iterator i = items_to_delete.begin(); i != items_to_delete.end(); i++) { + SPObject *item = reinterpret_cast<SPObject*>(*i); item->deleteObject(false); - items_to_select = g_slist_remove(items_to_select, item); + items_to_select.erase(remove(items_to_select.begin(), items_to_select.end(), item), items_to_select.end()); } - g_slist_free(items_to_delete); - - items_to_select = g_slist_reverse(items_to_select); selection->addList(items_to_select); - g_slist_free(items_to_select); if (apply_clip_path) { DocumentUndo::done(doc, SP_VERB_OBJECT_SET_CLIPPATH, _("Set clipping path")); @@ -4121,20 +4018,19 @@ void sp_selection_unset_mask(SPDesktop *desktop, bool apply_clip_path) { gchar const *attributeName = apply_clip_path ? "clip-path" : "mask"; std::map<SPObject*,SPItem*> referenced_objects; - GSList *items = g_slist_copy(const_cast<GSList *>(selection->itemList())); + std::vector<SPItem*> items(selection->itemList()); selection->clear(); GSList *items_to_ungroup = NULL; - GSList *items_to_select = g_slist_copy(items); - items_to_select = g_slist_reverse(items_to_select); + std::vector<SPItem*> items_to_select(items); // SPObject* refers to a group containing the clipped path or mask itself, // whereas SPItem* refers to the item being clipped or masked - for (GSList const *i = items; NULL != i; i = i->next) { + for (std::vector<SPItem*>::const_iterator i=items.begin();i!=items.end();i++){ if (remove_original) { // remember referenced mask/clippath, so orphaned masks can be moved back to document - SPItem *item = reinterpret_cast<SPItem *>(i->data); + SPItem *item = *i; Inkscape::URIReference *uri_ref = NULL; if (apply_clip_path) { @@ -4149,9 +4045,9 @@ void sp_selection_unset_mask(SPDesktop *desktop, bool apply_clip_path) { } } - SP_OBJECT(i->data)->getRepr()->setAttribute(attributeName, "none"); + (*i)->getRepr()->setAttribute(attributeName, "none"); - SPGroup *group = dynamic_cast<SPGroup *>(static_cast<SPObject *>(i->data)); + SPGroup *group = dynamic_cast<SPGroup *>(*i); if (ungroup_masked && group) { // if we had previously enclosed masked object in group, // add it to list so we can ungroup it later @@ -4163,7 +4059,6 @@ void sp_selection_unset_mask(SPDesktop *desktop, bool apply_clip_path) { } } - g_slist_free(items); // restore mask objects into a document for ( std::map<SPObject*,SPItem*>::iterator it = referenced_objects.begin() ; it != referenced_objects.end() ; ++it) { @@ -4171,7 +4066,7 @@ void sp_selection_unset_mask(SPDesktop *desktop, bool apply_clip_path) { GSList *items_to_move = NULL; for ( SPObject *child = obj->firstChild() ; child; child = child->getNext() ) { // Collect all clipped paths and masks within a single group - Inkscape::XML::Node *copy = SP_OBJECT(child)->getRepr()->duplicate(xml_doc); + Inkscape::XML::Node *copy = child->getRepr()->duplicate(xml_doc); if(copy->attribute("inkscape:original-d") && copy->attribute("inkscape:path-effect")) { copy->setAttribute("d", copy->attribute("inkscape:original-d")); @@ -4197,7 +4092,7 @@ void sp_selection_unset_mask(SPDesktop *desktop, bool apply_clip_path) { repr->setPosition((pos + 1) > 0 ? (pos + 1) : 0); SPItem *mask_item = static_cast<SPItem *>(desktop->getDocument()->getObjectByRepr(repr)); - items_to_select = g_slist_prepend(items_to_select, mask_item); + items_to_select.push_back(mask_item); // transform mask, so it is moved the same spot where mask was applied Geom::Affine transform(mask_item->transform); @@ -4212,10 +4107,10 @@ void sp_selection_unset_mask(SPDesktop *desktop, bool apply_clip_path) { for (GSList *i = items_to_ungroup ; NULL != i ; i = i->next) { SPGroup *group = dynamic_cast<SPGroup *>(static_cast<SPObject *>(i->data)); if (group) { - items_to_select = g_slist_remove(items_to_select, group); - GSList *children = NULL; - sp_item_group_ungroup(group, &children, false); - items_to_select = g_slist_concat(children, items_to_select); + items_to_select.erase(remove(items_to_select.begin(), items_to_select.end(), group), items_to_select.end()); + std::vector<SPItem*> children; + sp_item_group_ungroup(group, children, false); + items_to_select.insert(items_to_select.end(),children.rbegin(),children.rend()); } else { g_assert_not_reached(); } @@ -4224,9 +4119,7 @@ void sp_selection_unset_mask(SPDesktop *desktop, bool apply_clip_path) { g_slist_free(items_to_ungroup); // rebuild selection - items_to_select = g_slist_reverse(items_to_select); selection->addList(items_to_select); - g_slist_free(items_to_select); if (apply_clip_path) { DocumentUndo::done(doc, SP_VERB_OBJECT_UNSET_CLIPPATH, _("Release clipping path")); diff --git a/src/selection-chemistry.h b/src/selection-chemistry.h index 4ee7a5200..5bcc5b1ea 100644 --- a/src/selection-chemistry.h +++ b/src/selection-chemistry.h @@ -52,7 +52,7 @@ namespace LivePathEffect { } // namespace Inkscape void sp_selection_delete(SPDesktop *desktop); -void sp_selection_duplicate(SPDesktop *desktop, bool suppressDone = false); +void sp_selection_duplicate(SPDesktop *desktop, bool suppressDone = false, bool duplicateLayer = false); void sp_edit_clear_all(Inkscape::Selection *selection); void sp_edit_select_all(SPDesktop *desktop); @@ -143,8 +143,9 @@ enum SPSelectStrokeStyleType { void sp_select_same_fill_stroke_style(SPDesktop *desktop, gboolean fill, gboolean strok, gboolean style); void sp_select_same_object_type(SPDesktop *desktop); -GSList *sp_get_same_style(SPItem *sel, GSList *src, SPSelectStrokeStyleType type=SP_STYLE_ALL); -GSList *sp_get_same_object_type(SPItem *sel, GSList *src); + +std::vector<SPItem*> sp_get_same_style(SPItem *sel, std::vector<SPItem*> &src, SPSelectStrokeStyleType type=SP_STYLE_ALL); +std::vector<SPItem*> sp_get_same_object_type(SPItem *sel, std::vector<SPItem*> &src); void scroll_to_show_item(SPDesktop *desktop, SPItem *item); @@ -171,9 +172,9 @@ void unlock_all_in_all_layers(SPDesktop *dt); void unhide_all(SPDesktop *dt); void unhide_all_in_all_layers(SPDesktop *dt); -GSList *get_all_items(GSList *list, SPObject *from, SPDesktop *desktop, bool onlyvisible, bool onlysensitive, bool ingroups, GSList const *exclude); +std::vector<SPItem*> &get_all_items(std::vector<SPItem*> &list, SPObject *from, SPDesktop *desktop, bool onlyvisible, bool onlysensitive, bool ingroups, std::vector<SPItem*> const &exclude); -GSList *sp_degroup_list (GSList *items); +std::vector<SPItem*> sp_degroup_list (std::vector<SPItem*> &items); /* selection cycling */ typedef enum diff --git a/src/selection-describer.cpp b/src/selection-describer.cpp index 2386a731e..8304db684 100644 --- a/src/selection-describer.cpp +++ b/src/selection-describer.cpp @@ -40,14 +40,14 @@ #include "sp-spiral.h" // Returns a list of terms for the items to be used in the statusbar -char* collect_terms (GSList *items) +char* collect_terms (const std::vector<SPItem*> &items) { GSList *check = NULL; std::stringstream ss; bool first = true; - for (GSList *i = (GSList *)items; i != NULL; i = i->next) { - SPItem *item = dynamic_cast<SPItem *>(reinterpret_cast<SPObject *>(i->data)); + for ( std::vector<SPItem*>::const_iterator iter=items.begin();iter!=items.end();iter++ ) { + SPItem *item = *iter; if (item) { const char *term = item->displayName(); if (term != NULL && g_slist_find (check, term) == NULL) { @@ -61,12 +61,12 @@ char* collect_terms (GSList *items) } // Returns the number of terms in the list -static int count_terms (GSList *items) +static int count_terms (const std::vector<SPItem*> &items) { GSList *check = NULL; int count=0; - for (GSList *i = (GSList *)items; i != NULL; i = i->next) { - SPItem *item = dynamic_cast<SPItem *>(reinterpret_cast<SPObject *>(i->data)); + for ( std::vector<SPItem*>::const_iterator iter=items.begin();iter!=items.end();iter++ ) { + SPItem *item = *iter; if (item) { const char *term = item->displayName(); if (term != NULL && g_slist_find (check, term) == NULL) { @@ -79,11 +79,11 @@ static int count_terms (GSList *items) } // Returns the number of filtered items in the list -static int count_filtered (GSList *items) +static int count_filtered (const std::vector<SPItem*> &items) { int count=0; - for (GSList *i = items; i != NULL; i = i->next) { - SPItem *item = dynamic_cast<SPItem *>(reinterpret_cast<SPObject *>((i->data))); + for ( std::vector<SPItem*>::const_iterator iter=items.begin();iter!=items.end();iter++ ) { + SPItem *item = *iter; if (item) { count += item->isFiltered(); } @@ -122,12 +122,12 @@ void SelectionDescriber::_selectionModified(Inkscape::Selection *selection, guin } void SelectionDescriber::_updateMessageFromSelection(Inkscape::Selection *selection) { - GSList const *items = selection->itemList(); + std::vector<SPItem*> const items = selection->itemList(); - if (!items) { // no items + if (items.empty()) { // no items _context.set(Inkscape::NORMAL_MESSAGE, _when_nothing); } else { - SPItem *item = dynamic_cast<SPItem *>(reinterpret_cast<SPObject *>(items->data)); + SPItem *item = items[0]; g_assert(item != NULL); SPObject *layer = selection->layers()->layerForObject(item); SPObject *root = selection->layers()->currentRoot(); @@ -188,7 +188,7 @@ void SelectionDescriber::_updateMessageFromSelection(Inkscape::Selection *select g_free (layer_name); g_free (parent_name); - if (!items->next) { // one item + if (items.size()==1) { // one item char *item_desc = item->detailedDescription(); bool isUse = dynamic_cast<SPUse *>(item) != NULL; @@ -228,9 +228,9 @@ void SelectionDescriber::_updateMessageFromSelection(Inkscape::Selection *select g_free(item_desc); } else { // multiple items - int objcount = g_slist_length((GSList *)items); - char *terms = collect_terms ((GSList *)items); - int n_terms = count_terms((GSList *)items); + int objcount = items.size(); + char *terms = collect_terms (items); + int n_terms = count_terms(items); gchar *objects_str = g_strdup_printf(ngettext( "<b>%1$i</b> objects selected of type %2$s", @@ -241,7 +241,7 @@ void SelectionDescriber::_updateMessageFromSelection(Inkscape::Selection *select // indicate all, some, or none filtered gchar *filt_str = NULL; - int n_filt = count_filtered((GSList *)items); //all filtered + int n_filt = count_filtered(items); //all filtered if (n_filt) { filt_str = g_strdup_printf(ngettext("; <i>%d filtered object</i> ", "; <i>%d filtered objects</i> ", n_filt), n_filt); diff --git a/src/selection.cpp b/src/selection.cpp index 81139d044..77a507eec 100644 --- a/src/selection.cpp +++ b/src/selection.cpp @@ -42,9 +42,11 @@ namespace Inkscape { Selection::Selection(LayerModel *layers, SPDesktop *desktop) : - _objs(NULL), - _reprs(NULL), - _items(NULL), + _objs(std::list<SPObject*>()), + _objs_vector(std::vector<SPObject*>()), + _objs_set(std::set<SPObject*>()), + _reprs(std::vector<XML::Node*>()), + _items(std::vector<SPItem*>()), _layers(layers), _desktop(desktop), _selection_context(NULL), @@ -121,17 +123,15 @@ Selection::_releaseContext(SPObject *obj) } void Selection::_invalidateCachedLists() { - g_slist_free(_items); - _items = NULL; - - g_slist_free(_reprs); - _reprs = NULL; + _items.clear(); + _reprs.clear(); + _objs_vector.clear(); } void Selection::_clear() { _invalidateCachedLists(); - while (_objs) { - SPObject *obj=reinterpret_cast<SPObject *>(_objs->data); + while (!_objs.empty()) { + SPObject *obj=_objs.front(); _remove(obj); } } @@ -148,7 +148,7 @@ bool Selection::includes(SPObject *obj) const { g_return_val_if_fail(SP_IS_OBJECT(obj), FALSE); - return ( g_slist_find(_objs, obj) != NULL ); + return ( _objs_set.find(obj)!=_objs_set.end() ); } void Selection::add(SPObject *obj, bool persist_selection_context/* = false */) { @@ -179,8 +179,10 @@ void Selection::_add(SPObject *obj) { // (to prevent double-selection) _removeObjectDescendants(obj); _removeObjectAncestors(obj); + g_return_if_fail(SP_IS_OBJECT(obj)); - _objs = g_slist_prepend(_objs, obj); + _objs.push_front(obj); + _objs_set.insert(obj); add_3D_boxes_recursively(obj); @@ -234,26 +236,27 @@ void Selection::_remove(SPObject *obj) { remove_3D_boxes_recursively(obj); - _objs = g_slist_remove(_objs, obj); + _objs.remove(obj); + _objs_set.erase(obj); } -void Selection::setList(GSList const *list) { +void Selection::setList(std::vector<SPItem*> const &list) { // Clear and add, or just clear with emit. - if (list != NULL) { + if (!list.empty()) { _clear(); addList(list); } else clear(); } -void Selection::addList(GSList const *list) { +void Selection::addList(std::vector<SPItem*> const &list) { - if (list == NULL) + if (list.empty()) return; _invalidateCachedLists(); - for ( GSList const *iter = list ; iter != NULL ; iter = iter->next ) { - SPObject *obj = reinterpret_cast<SPObject *>(iter->data); + for ( std::vector<SPItem*>::const_iterator iter=list.begin();iter!=list.end();iter++ ) { + SPObject *obj = *iter; if (includes(obj)) continue; _add (obj); } @@ -261,11 +264,11 @@ void Selection::addList(GSList const *list) { _emitChanged(); } -void Selection::setReprList(GSList const *list) { +void Selection::setReprList(std::vector<XML::Node*> const &list) { _clear(); - for ( GSList const *iter = list ; iter != NULL ; iter = iter->next ) { - SPObject *obj=_objectForXMLNode(reinterpret_cast<Inkscape::XML::Node *>(iter->data)); + for ( std::vector<XML::Node*>::const_reverse_iterator iter=list.rbegin();iter!=list.rend();iter++ ) { + SPObject *obj=_objectForXMLNode(*iter); if (obj) { _add(obj); } @@ -279,35 +282,38 @@ void Selection::clear() { _emitChanged(); } -GSList const *Selection::list() { - return _objs; +std::vector<SPObject*> const &Selection::list() { + if(!_objs_vector.empty()) + return _objs_vector; + + for ( std::list<SPObject*>::const_iterator iter=_objs.begin();iter!=_objs.end();iter++ ) { + _objs_vector.push_back(*iter); + } + return _objs_vector; + } -GSList const *Selection::itemList() { - if (_items) { +std::vector<SPItem*> const &Selection::itemList() { + if (!_items.empty()) { return _items; } - for ( GSList const *iter=_objs ; iter != NULL ; iter = iter->next ) { - SPObject *obj=reinterpret_cast<SPObject *>(iter->data); + for ( std::list<SPObject*>::const_iterator iter=_objs.begin();iter!=_objs.end();iter++ ) { + SPObject *obj=*iter; if (SP_IS_ITEM(obj)) { - _items = g_slist_prepend(_items, SP_ITEM(obj)); + _items.push_back(SP_ITEM(obj)); } } - _items = g_slist_reverse(_items); - return _items; } -GSList const *Selection::reprList() { - if (_reprs) { return _reprs; } - - for ( GSList const *iter=itemList() ; iter != NULL ; iter = iter->next ) { - SPObject *obj=reinterpret_cast<SPObject *>(iter->data); - _reprs = g_slist_prepend(_reprs, obj->getRepr()); +std::vector<XML::Node*> const &Selection::reprList() { + if (!_reprs.empty()) { return _reprs; } + std::vector<SPItem*> list = itemList(); + for ( std::vector<SPItem*>::const_iterator iter=list.begin();iter!=list.end();iter++ ) { + SPObject *obj = *iter; + _reprs.push_back(obj->getRepr()); } - _reprs = g_slist_reverse(_reprs); - return _reprs; } @@ -337,17 +343,17 @@ std::list<SPBox3D *> const Selection::box3DList(Persp3D *persp) { } SPObject *Selection::single() { - if ( _objs != NULL && _objs->next == NULL ) { - return reinterpret_cast<SPObject *>(_objs->data); + if ( _objs.size() == 1 ) { + return _objs.front(); } else { return NULL; } } SPItem *Selection::singleItem() { - GSList const *items=itemList(); - if ( items != NULL && items->next == NULL ) { - return reinterpret_cast<SPItem *>(items->data); + std::vector<SPItem*> const items=itemList(); + if ( items.size()==1) { + return items[0]; } else { return NULL; } @@ -362,12 +368,12 @@ SPItem *Selection::largestItem(Selection::CompareSize compare) { } SPItem *Selection::_sizeistItem(bool sml, Selection::CompareSize compare) { - GSList const *items = const_cast<Selection *>(this)->itemList(); + std::vector<SPItem*> const items = const_cast<Selection *>(this)->itemList(); gdouble max = sml ? 1e18 : 0; SPItem *ist = NULL; - for ( GSList const *i = items; i != NULL ; i = i->next ) { - Geom::OptRect obox = SP_ITEM(i->data)->desktopPreferredBounds(); + for ( std::vector<SPItem*>::const_iterator i=items.begin();i!=items.end();i++ ) { + Geom::OptRect obox = SP_ITEM(*i)->desktopPreferredBounds(); if (!obox || obox.isEmpty()) continue; Geom::Rect bbox = *obox; @@ -376,7 +382,7 @@ SPItem *Selection::_sizeistItem(bool sml, Selection::CompareSize compare) { size = sml ? size : size * -1; if (size < max) { max = size; - ist = SP_ITEM(i->data); + ist = SP_ITEM(*i); } } return ist; @@ -395,22 +401,22 @@ Geom::OptRect Selection::bounds(SPItem::BBoxType type) const Geom::OptRect Selection::geometricBounds() const { - GSList const *items = const_cast<Selection *>(this)->itemList(); + std::vector<SPItem*> const items = const_cast<Selection *>(this)->itemList(); Geom::OptRect bbox; - for ( GSList const *i = items ; i != NULL ; i = i->next ) { - bbox.unionWith(SP_ITEM(i->data)->desktopGeometricBounds()); + for ( std::vector<SPItem*>::const_iterator iter=items.begin();iter!=items.end();iter++ ) { + bbox.unionWith(SP_ITEM(*iter)->desktopGeometricBounds()); } return bbox; } Geom::OptRect Selection::visualBounds() const { - GSList const *items = const_cast<Selection *>(this)->itemList(); + std::vector<SPItem*> const items = const_cast<Selection *>(this)->itemList(); Geom::OptRect bbox; - for ( GSList const *i = items ; i != NULL ; i = i->next ) { - bbox.unionWith(SP_ITEM(i->data)->desktopVisualBounds()); + for ( std::vector<SPItem*>::const_iterator iter=items.begin();iter!=items.end();iter++ ) { + bbox.unionWith(SP_ITEM(*iter)->desktopVisualBounds()); } return bbox; } @@ -427,11 +433,11 @@ Geom::OptRect Selection::preferredBounds() const Geom::OptRect Selection::documentBounds(SPItem::BBoxType type) const { Geom::OptRect bbox; - GSList const *items = const_cast<Selection *>(this)->itemList(); - if (!items) return bbox; + std::vector<SPItem*> const items = const_cast<Selection *>(this)->itemList(); + if (items.empty()) return bbox; - for ( GSList const *iter=items ; iter != NULL ; iter = iter->next ) { - SPItem *item = SP_ITEM(iter->data); + for ( std::vector<SPItem*>::const_iterator iter=items.begin();iter!=items.end();iter++ ) { + SPItem *item = SP_ITEM(*iter); bbox |= item->documentBounds(type); } @@ -441,9 +447,9 @@ Geom::OptRect Selection::documentBounds(SPItem::BBoxType type) const // If we have a selection of multiple items, then the center of the first item // will be returned; this is also the case in SelTrans::centerRequest() boost::optional<Geom::Point> Selection::center() const { - GSList *items = (GSList *) const_cast<Selection *>(this)->itemList(); - if (items) { - SPItem *first = reinterpret_cast<SPItem*>(g_slist_last(items)->data); // from the first item in selection + std::vector<SPItem*> const items = const_cast<Selection *>(this)->itemList(); + if (!items.empty()) { + SPItem *first = items.back(); // from the first item in selection if (first->isCenterSet()) { // only if set explicitly return first->getCenter(); } @@ -457,13 +463,13 @@ boost::optional<Geom::Point> Selection::center() const { } std::vector<Inkscape::SnapCandidatePoint> Selection::getSnapPoints(SnapPreferences const *snapprefs) const { - GSList const *items = const_cast<Selection *>(this)->itemList(); + std::vector<SPItem*> const items = const_cast<Selection *>(this)->itemList(); SnapPreferences snapprefs_dummy = *snapprefs; // create a local copy of the snapping prefs snapprefs_dummy.setTargetSnappable(Inkscape::SNAPTARGET_ROTATION_CENTER, false); // locally disable snapping to the item center std::vector<Inkscape::SnapCandidatePoint> p; - for (GSList const *iter = items; iter != NULL; iter = iter->next) { - SPItem *this_item = SP_ITEM(iter->data); + for ( std::vector<SPItem*>::const_iterator iter=items.begin();iter!=items.end();iter++ ) { + SPItem *this_item = *iter; this_item->getSnappoints(p, &snapprefs_dummy); //Include the transformation origin for snapping @@ -477,19 +483,21 @@ std::vector<Inkscape::SnapCandidatePoint> Selection::getSnapPoints(SnapPreferenc } void Selection::_removeObjectDescendants(SPObject *obj) { - GSList *iter, *next; - for ( iter = _objs ; iter ; iter = next ) { - next = iter->next; - SPObject *sel_obj=reinterpret_cast<SPObject *>(iter->data); + std::vector<SPObject*> toremove; + for ( std::list<SPObject*>::const_iterator iter=_objs.begin();iter!=_objs.end();iter++ ) { + SPObject *sel_obj= dynamic_cast<SPObject*>(*iter); SPObject *parent = sel_obj->parent; while (parent) { if ( parent == obj ) { - _remove(sel_obj); + toremove.push_back(sel_obj); break; } parent = parent->parent; } } + for ( std::vector<SPObject*>::const_iterator iter=toremove.begin();iter!=toremove.end();iter++ ) { + _remove(*iter); + } } void Selection::_removeObjectAncestors(SPObject *obj) { @@ -511,32 +519,24 @@ SPObject *Selection::_objectForXMLNode(Inkscape::XML::Node *repr) const { return object; } -guint Selection::numberOfLayers() { - GSList const *items = const_cast<Selection *>(this)->itemList(); - GSList *layers = NULL; - for (GSList const *iter = items; iter != NULL; iter = iter->next) { - SPObject *layer = _layers->layerForObject(SP_OBJECT(iter->data)); - if (g_slist_find (layers, layer) == NULL) { - layers = g_slist_prepend (layers, layer); - } +size_t Selection::numberOfLayers() { + std::vector<SPItem*> const items = const_cast<Selection *>(this)->itemList(); + std::set<SPObject*> layers; + for ( std::vector<SPItem*>::const_iterator iter=items.begin();iter!=items.end();iter++ ) { + SPObject *layer = _layers->layerForObject(*iter); + layers.insert(layer); } - guint ret = g_slist_length (layers); - g_slist_free (layers); - return ret; -} - -guint Selection::numberOfParents() { - GSList const *items = const_cast<Selection *>(this)->itemList(); - GSList *parents = NULL; - for (GSList const *iter = items; iter != NULL; iter = iter->next) { - SPObject *parent = SP_OBJECT(iter->data)->parent; - if (g_slist_find (parents, parent) == NULL) { - parents = g_slist_prepend (parents, parent); - } + return layers.size(); +} + +size_t Selection::numberOfParents() { + std::vector<SPItem*> const items = const_cast<Selection *>(this)->itemList(); + std::set<SPObject*> parents; + for ( std::vector<SPItem*>::const_iterator iter=items.begin();iter!=items.end();iter++ ) { + SPObject *parent = (*iter)->parent; + parents.insert(parent); } - guint ret = g_slist_length (parents); - g_slist_free (parents); - return ret; + return parents.size(); } } diff --git a/src/selection.h b/src/selection.h index 19e2ae1fd..952dde51d 100644 --- a/src/selection.h +++ b/src/selection.h @@ -16,6 +16,7 @@ #include <vector> #include <map> #include <list> +#include <set> #include <stddef.h> #include <sigc++/sigc++.h> @@ -26,11 +27,11 @@ #include "sp-item.h" + class SPDesktop; class SPItem; class SPBox3D; class Persp3D; -typedef struct _GSList GSList; namespace Inkscape { class LayerModel; @@ -39,6 +40,7 @@ class Node; } } + namespace Inkscape { /** @@ -154,21 +156,21 @@ public: * * @param objs the objects to select */ - void setList(GSList const *objs); + void setList(std::vector<SPItem*> const &objs); /** * Adds the specified objects to selection, without deselecting first. * * @param objs the objects to select */ - void addList(GSList const *objs); + void addList(std::vector<SPItem*> const &objs); /** * Clears the selection and selects the specified objects. * * @param repr a list of xml nodes for the items to select */ - void setReprList(GSList const *reprs); + void setReprList(std::vector<XML::Node*> const &reprs); /** Add items from an STL iterator range to the selection. * \param from the begin iterator @@ -192,7 +194,7 @@ public: /** * Returns true if no items are selected. */ - bool isEmpty() const { return _objs == NULL; } + bool isEmpty() const { return _objs.empty(); } /** * Returns true if the given object is selected. @@ -238,13 +240,13 @@ public: XML::Node *singleRepr(); /** Returns the list of selected objects. */ - GSList const *list(); + std::vector<SPObject*> const &list(); /** Returns the list of selected SPItems. */ - GSList const *itemList(); + std::vector<SPItem*> const &itemList(); /** Returns a list of the xml nodes of all selected objects. */ /// \todo only returns reprs of SPItems currently; need a separate /// method for that - GSList const *reprList(); + std::vector<XML::Node*> const &reprList(); /** Returns a list of all perspectives which have a 3D box in the current selection. (these may also be nested in groups) */ @@ -257,10 +259,10 @@ public: std::list<SPBox3D *> const box3DList(Persp3D *persp = NULL); /** Returns the number of layers in which there are selected objects. */ - unsigned int numberOfLayers(); + size_t numberOfLayers(); /** Returns the number of parents to which the selected objects belong. */ - unsigned int numberOfParents(); + size_t numberOfParents(); /** Returns the bounding rectangle of the selection. */ Geom::OptRect bounds(SPItem::BBoxType type) const; @@ -360,9 +362,11 @@ private: /** Releases an active layer object that is being removed. */ void _releaseContext(SPObject *obj); - mutable GSList *_objs; - mutable GSList *_reprs; - mutable GSList *_items; + mutable std::list<SPObject*> _objs; //to more efficiently remove arbitrary elements + mutable std::vector<SPObject*> _objs_vector; // to be returned by list(); + mutable std::set<SPObject*> _objs_set; //to efficiently test if object is selected + mutable std::vector<XML::Node*> _reprs; + mutable std::vector<SPItem*> _items; void add_box_perspective(SPBox3D *box); void add_3D_boxes_recursively(SPObject *obj); diff --git a/src/seltrans.cpp b/src/seltrans.cpp index 5e4c0642e..f7562923f 100644 --- a/src/seltrans.cpp +++ b/src/seltrans.cpp @@ -112,7 +112,7 @@ Inkscape::SelTrans::SelTrans(SPDesktop *desktop) : _opposite_for_bboxpoints(Geom::Point(0,0)), _origin_for_specpoints(Geom::Point(0,0)), _origin_for_bboxpoints(Geom::Point(0,0)), - _stamp_cache(NULL), + _stamp_cache(std::vector<SPItem*>()), _message_context(desktop->messageStack()), _bounding_box_prefs_observer(*this) { @@ -239,8 +239,9 @@ void Inkscape::SelTrans::setCenter(Geom::Point const &p) _center_is_set = true; // Write the new center position into all selected items - for (GSList const *l = _desktop->selection->itemList(); l; l = l->next) { - SPItem *it = SP_ITEM(l->data); + std::vector<SPItem*> items=_desktop->selection->itemList(); + for ( std::vector<SPItem*>::const_iterator iter=items.begin();iter!=items.end();iter++ ) { + SPItem *it = SP_ITEM(*iter); it->setCenter(p); // only set the value; updating repr and document_done will be done once, on ungrab } @@ -268,8 +269,9 @@ void Inkscape::SelTrans::grab(Geom::Point const &p, gdouble x, gdouble y, bool s return; } - for (GSList const *l = selection->itemList(); l; l = l->next) { - SPItem *it = reinterpret_cast<SPItem*>(sp_object_ref(SP_ITEM(l->data), NULL)); + std::vector<SPItem*> items=_desktop->selection->itemList(); + for ( std::vector<SPItem*>::const_iterator iter=items.begin();iter!=items.end();iter++ ) { + SPItem *it = static_cast<SPItem*>(sp_object_ref(*iter, NULL)); _items.push_back(it); _items_const.push_back(it); _items_affines.push_back(it->i2dt_affine()); @@ -370,7 +372,7 @@ void Inkscape::SelTrans::grab(Geom::Point const &p, gdouble x, gdouble y, bool s } _updateHandles(); - g_return_if_fail(_stamp_cache == NULL); + g_return_if_fail(_stamp_cache.empty()); } void Inkscape::SelTrans::transform(Geom::Affine const &rel_affine, Geom::Point const &norm) @@ -432,10 +434,8 @@ void Inkscape::SelTrans::ungrab() for (int i = 0; i < 4; i++) sp_canvas_item_hide(_l[i]); } - - if (_stamp_cache) { - g_slist_free(_stamp_cache); - _stamp_cache = NULL; + if(!_stamp_cache.empty()){ + _stamp_cache.clear(); } _message_context.clear(); @@ -491,8 +491,9 @@ void Inkscape::SelTrans::ungrab() if (_center_is_set) { // we were dragging center; update reprs and commit undoable action - for (GSList const *l = _desktop->selection->itemList(); l; l = l->next) { - SPItem *it = SP_ITEM(l->data); + std::vector<SPItem*> items=_desktop->selection->itemList(); + for ( std::vector<SPItem*>::const_iterator iter=items.begin();iter!=items.end();iter++ ) { + SPItem *it = *iter; it->updateRepr(); } DocumentUndo::done(_desktop->getDocument(), SP_VERB_CONTEXT_SELECT, @@ -515,26 +516,25 @@ void Inkscape::SelTrans::stamp() Inkscape::Selection *selection = _desktop->getSelection(); bool fixup = !_grabbed; - if ( fixup && _stamp_cache ) { + if ( fixup && !_stamp_cache.empty() ) { // TODO - give a proper fix. Simple temporary work-around for the grab() issue - g_slist_free(_stamp_cache); - _stamp_cache = NULL; + _stamp_cache.clear(); } /* stamping mode */ if (!_empty) { - GSList *l; - if (_stamp_cache) { + std::vector<SPItem*> l; + if (!_stamp_cache.empty()) { l = _stamp_cache; } else { /* Build cache */ - l = g_slist_copy((GSList *) selection->itemList()); - l = g_slist_sort(l, (GCompareFunc) sp_object_compare_position); + l = selection->itemList(); + sort(l.begin(),l.end(),sp_object_compare_position_bool); _stamp_cache = l; } - while (l) { - SPItem *original_item = SP_ITEM(l->data); + for(std::vector<SPItem*>::const_iterator x=l.begin();x!=l.end();x++) { + SPItem *original_item = *x; Inkscape::XML::Node *original_repr = original_item->getRepr(); // remember the position of the item @@ -568,16 +568,14 @@ void Inkscape::SelTrans::stamp() } Inkscape::GC::release(copy_repr); - l = l->next; } DocumentUndo::done(_desktop->getDocument(), SP_VERB_CONTEXT_SELECT, _("Stamp")); } - if ( fixup && _stamp_cache ) { + if ( fixup && !_stamp_cache.empty() ) { // TODO - give a proper fix. Simple temporary work-around for the grab() issue - g_slist_free(_stamp_cache); - _stamp_cache = NULL; + _stamp_cache.clear(); } } @@ -712,8 +710,9 @@ void Inkscape::SelTrans::handleClick(SPKnot */*knot*/, guint state, SPSelTransHa case HANDLE_CENTER: if (state & GDK_SHIFT_MASK) { // Unset the center position for all selected items - for (GSList const *l = _desktop->selection->itemList(); l; l = l->next) { - SPItem *it = SP_ITEM(l->data); + std::vector<SPItem*> items=_desktop->selection->itemList(); + for ( std::vector<SPItem*>::const_iterator iter=items.begin();iter!=items.end();iter++ ) { + SPItem *it = *iter; it->unsetCenter(); it->updateRepr(); _center_is_set = false; // center has changed @@ -1283,7 +1282,7 @@ gboolean Inkscape::SelTrans::centerRequest(Geom::Point &pt, guint state) // items will share a single center. While dragging that single center, it should never snap to the // centers of any of the selected objects. Therefore we will have to pass the list of selected items // to the snapper, to avoid self-snapping of the rotation center - GSList *items = (GSList *) const_cast<Selection *>(_selection)->itemList(); + std::vector<SPItem*> items = const_cast<Selection *>(_selection)->itemList(); SnapManager &m = _desktop->namedview->snap_manager; m.setup(_desktop); m.setRotationCenterSource(items); diff --git a/src/seltrans.h b/src/seltrans.h index d5db1542d..26c2e9cd9 100644 --- a/src/seltrans.h +++ b/src/seltrans.h @@ -187,7 +187,7 @@ private: SPCtrlLine *_l[4]; unsigned int _sel_changed_id; unsigned int _sel_modified_id; - GSList *_stamp_cache; + std::vector<SPItem*> _stamp_cache; Geom::Point _origin; ///< position of origin for transforms Geom::Point _point; ///< original position of the knot being used for the current transform diff --git a/src/snap-enums.h b/src/snap-enums.h index c6ca97402..ab82aaf37 100644 --- a/src/snap-enums.h +++ b/src/snap-enums.h @@ -66,7 +66,7 @@ enum SnapTargetType { SNAPTARGET_NODE_SMOOTH, SNAPTARGET_NODE_CUSP, SNAPTARGET_LINE_MIDPOINT, - SNAPTARGET_PATH, + SNAPTARGET_PATH, // If path targets are added here, then also add them to the list in findBestSnap() SNAPTARGET_PATH_PERPENDICULAR, SNAPTARGET_PATH_TANGENTIAL, SNAPTARGET_PATH_INTERSECTION, diff --git a/src/snap.cpp b/src/snap.cpp index 96b5ab53c..5a308777c 100644 --- a/src/snap.cpp +++ b/src/snap.cpp @@ -43,7 +43,7 @@ SnapManager::SnapManager(SPNamedView const *v) : object(this, 0), snapprefs(), _named_view(v), - _rotation_center_source_items(NULL), + _rotation_center_source_items(std::vector<SPItem*>()), _guide_to_ignore(NULL), _desktop(NULL), _snapindicator(true), @@ -121,7 +121,8 @@ void SnapManager::freeSnapReturnByRef(Geom::Point &p, } Inkscape::SnappedPoint SnapManager::freeSnap(Inkscape::SnapCandidatePoint const &p, - Geom::OptRect const &bbox_to_snap) const + Geom::OptRect const &bbox_to_snap, + bool to_paths_only) const { if (!someSnapperMightSnap()) { return Inkscape::SnappedPoint(p, Inkscape::SNAPTARGET_UNDEFINED, Geom::infinity(), 0, false, false, false); @@ -134,16 +135,16 @@ Inkscape::SnappedPoint SnapManager::freeSnap(Inkscape::SnapCandidatePoint const (*i)->freeSnap(isr, p, bbox_to_snap, &_items_to_ignore, _unselected_nodes); } - return findBestSnap(p, isr, false); + return findBestSnap(p, isr, false, false, to_paths_only); } -void SnapManager::preSnap(Inkscape::SnapCandidatePoint const &p) +void SnapManager::preSnap(Inkscape::SnapCandidatePoint const &p, bool to_paths_only) { // setup() must have been called before calling this method! if (_snapindicator) { _snapindicator = false; // prevent other methods from drawing a snap indicator; we want to control this here - Inkscape::SnappedPoint s = freeSnap(p); + Inkscape::SnappedPoint s = freeSnap(p, Geom::OptRect(), to_paths_only); g_assert(_desktop != NULL); if (s.getSnapped()) { _desktop->snapindicator->set_new_snaptarget(s, true); @@ -855,7 +856,8 @@ Inkscape::SnappedPoint SnapManager::constrainedSnapRotate(std::vector<Inkscape:: Inkscape::SnappedPoint SnapManager::findBestSnap(Inkscape::SnapCandidatePoint const &p, IntermSnapResults const &isr, bool constrained, - bool allowOffScreen) const + bool allowOffScreen, + bool to_path_only) const { g_assert(_desktop != NULL); @@ -966,6 +968,30 @@ Inkscape::SnappedPoint SnapManager::findBestSnap(Inkscape::SnapCandidatePoint co } } + // Filter out all snap targets that do NOT include a path; this is useful when we try to insert + // a node in a path (on doubleclick in the node tool). We don't want to change the shape of the + // path, so the snapped point must be on a path, and not e.g. on a grid intersection + if (to_path_only) { + std::list<Inkscape::SnappedPoint>::iterator i = sp_list.begin(); + + while (i != sp_list.end()) { + Inkscape::SnapTargetType t = (*i).getTarget(); + if (t == Inkscape::SNAPTARGET_LINE_MIDPOINT || + t == Inkscape::SNAPTARGET_PATH || + t == Inkscape::SNAPTARGET_PATH_PERPENDICULAR || + t == Inkscape::SNAPTARGET_PATH_TANGENTIAL || + t == Inkscape::SNAPTARGET_PATH_INTERSECTION || + t == Inkscape::SNAPTARGET_PATH_GUIDE_INTERSECTION || + t == Inkscape::SNAPTARGET_PATH_CLIP || + t == Inkscape::SNAPTARGET_PATH_MASK || + t == Inkscape::SNAPTARGET_ELLIPSE_QUADRANT_POINT) { + ++i; + } else { + i = sp_list.erase(i); + } + } + } + // now let's see which snapped point gets a thumbs up Inkscape::SnappedPoint bestSnappedPoint(p.getPoint()); // std::cout << "Finding the best snap..." << std::endl; @@ -1013,7 +1039,7 @@ void SnapManager::setup(SPDesktop const *desktop, _snapindicator = snapindicator; _unselected_nodes = unselected_nodes; _guide_to_ignore = guide_to_ignore; - _rotation_center_source_items = NULL; + _rotation_center_source_items.clear(); } void SnapManager::setup(SPDesktop const *desktop, @@ -1031,7 +1057,7 @@ void SnapManager::setup(SPDesktop const *desktop, _snapindicator = snapindicator; _unselected_nodes = unselected_nodes; _guide_to_ignore = guide_to_ignore; - _rotation_center_source_items = NULL; + _rotation_center_source_items.clear(); } /// Setup, taking the list of items to ignore from the desktop's selection. @@ -1049,13 +1075,13 @@ void SnapManager::setupIgnoreSelection(SPDesktop const *desktop, _snapindicator = snapindicator; _unselected_nodes = unselected_nodes; _guide_to_ignore = guide_to_ignore; - _rotation_center_source_items = NULL; + _rotation_center_source_items.clear(); _items_to_ignore.clear(); Inkscape::Selection *sel = _desktop->selection; - GSList const *items = sel->itemList(); - for (GSList *i = const_cast<GSList*>(items); i; i = i->next) { - _items_to_ignore.push_back(static_cast<SPItem const *>(i->data)); + std::vector<SPItem*> const items = sel->itemList(); + for (std::vector<SPItem*>::const_iterator i=items.begin();i!=items.end();i++) { + _items_to_ignore.push_back(*i); } } diff --git a/src/snap.h b/src/snap.h index 20b2b246f..944c49b3e 100644 --- a/src/snap.h +++ b/src/snap.h @@ -136,7 +136,7 @@ public: std::vector<Inkscape::SnapCandidatePoint> *unselected_nodes = NULL, SPGuide *guide_to_ignore = NULL); - void unSetup() {_rotation_center_source_items = NULL; + void unSetup() {_rotation_center_source_items.clear(); _guide_to_ignore = NULL; _desktop = NULL; _unselected_nodes = NULL;} @@ -145,8 +145,8 @@ public: // of this rotation center; this reference is used to make sure that we do not snap a rotation // center to itself // NOTE: Must be called after calling setup(), not before! - void setRotationCenterSource(GSList *items) {_rotation_center_source_items = items;} - GSList const *getRotationCenterSource() {return _rotation_center_source_items;} + void setRotationCenterSource(const std::vector<SPItem*> &items) {_rotation_center_source_items = items;} + const std::vector<SPItem*> &getRotationCenterSource() {return _rotation_center_source_items;} // freeSnapReturnByRef() is preferred over freeSnap(), because it only returns a // point if snapping has occurred (by overwriting p); otherwise p is untouched @@ -192,12 +192,15 @@ public: * * @param p Source point to be snapped. * @param bbox_to_snap Bounding box hulling the set of points, all from the same selection and having the same transformation. + * @param to_path_only Only snap to points on a path, such as path intersections with itself or with grids/guides. This is used for + * example when adding nodes to a path. We will not snap for example to grid intersections * @return An instance of the SnappedPoint class, which holds data on the snap source, snap target, and various metrics. */ Inkscape::SnappedPoint freeSnap(Inkscape::SnapCandidatePoint const &p, - Geom::OptRect const &bbox_to_snap = Geom::OptRect() ) const; + Geom::OptRect const &bbox_to_snap = Geom::OptRect(), + bool to_path_only = false) const; - void preSnap(Inkscape::SnapCandidatePoint const &p); + void preSnap(Inkscape::SnapCandidatePoint const &p, bool to_path_only = false); /** * Snap to the closest multiple of a grid pitch. @@ -473,9 +476,11 @@ public: * @param isr A structure holding all snap targets that have been found so far. * @param constrained True if the snap is constrained, e.g. for stretching or for purely horizontal translation. * @param allowOffScreen If true, then snapping to points which are off the screen is allowed (needed for example when pasting to the grid). + * @param to_path_only Only snap to points on a path, such as path intersections with itself or with grids/guides. This is used for + * example when adding nodes to a path. We will not snap for example to grid intersections * @return An instance of the SnappedPoint class, which holds data on the snap source, snap target, and various metrics. */ - Inkscape::SnappedPoint findBestSnap(Inkscape::SnapCandidatePoint const &p, IntermSnapResults const &isr, bool constrained, bool allowOffScreen = false) const; + Inkscape::SnappedPoint findBestSnap(Inkscape::SnapCandidatePoint const &p, IntermSnapResults const &isr, bool constrained, bool allowOffScreen = false, bool to_paths_only = false) const; /** * Mark the location of the snap source (not the snap target!) on the canvas by drawing a symbol. @@ -490,7 +495,7 @@ protected: private: std::vector<SPItem const *> _items_to_ignore; ///< Items that should not be snapped to, for example the items that are currently being dragged. Set using the setup() method - GSList *_rotation_center_source_items; // to avoid snapping a rotation center to itself + std::vector<SPItem*> _rotation_center_source_items; // to avoid snapping a rotation center to itself SPGuide *_guide_to_ignore; ///< A guide that should not be snapped to, e.g. the guide that is currently being dragged SPDesktop const *_desktop; bool _snapindicator; ///< When true, an indicator will be drawn at the position that was being snapped to diff --git a/src/sp-clippath.cpp b/src/sp-clippath.cpp index 3c6167438..d66508eae 100644 --- a/src/sp-clippath.cpp +++ b/src/sp-clippath.cpp @@ -296,7 +296,7 @@ sp_clippath_view_list_remove(SPClipPathView *list, SPClipPathView *view) } // Create a mask element (using passed elements), add it to <defs> -const gchar *SPClipPath::create (GSList *reprs, SPDocument *document, Geom::Affine const* applyTransform) +const gchar *SPClipPath::create (std::vector<Inkscape::XML::Node*> &reprs, SPDocument *document, Geom::Affine const* applyTransform) { Inkscape::XML::Node *defsrepr = document->getDefs()->getRepr(); @@ -308,13 +308,12 @@ const gchar *SPClipPath::create (GSList *reprs, SPDocument *document, Geom::Affi const gchar *id = repr->attribute("id"); SPObject *clip_path_object = document->getObjectById(id); - for (GSList *it = reprs; it != NULL; it = it->next) { - Inkscape::XML::Node *node = (Inkscape::XML::Node *)(it->data); + for (std::vector<Inkscape::XML::Node*>::const_iterator it = reprs.begin(); it != reprs.end(); it++) { + Inkscape::XML::Node *node = (*it); SPItem *item = SP_ITEM(clip_path_object->appendChildRepr(node)); if (NULL != applyTransform) { - Geom::Affine transform (item->transform); - transform *= (*applyTransform); + Geom::Affine transform (item->transform * (*applyTransform)); item->doWriteTransform(item->getRepr(), transform); } } diff --git a/src/sp-clippath.h b/src/sp-clippath.h index eb8b14174..c9a8c68df 100644 --- a/src/sp-clippath.h +++ b/src/sp-clippath.h @@ -45,7 +45,7 @@ public: unsigned int clipPathUnits : 1; SPClipPathView *display; - static char const *create(GSList *reprs, SPDocument *document, Geom::Affine const* applyTransform); + static char const *create(std::vector<Inkscape::XML::Node*> &reprs, SPDocument *document, Geom::Affine const* applyTransform); //static GType sp_clippath_get_type(void); Inkscape::DrawingItem *show(Inkscape::Drawing &drawing, unsigned int key); @@ -88,7 +88,7 @@ protected: return false; } SPObject * const owner = this->getOwner(); - if (obj->isAncestorOf(owner)) { + if (!URIReference::_acceptObject(obj)) { //XML Tree being used directly here while it shouldn't be... Inkscape::XML::Node * const owner_repr = owner->getRepr(); //XML Tree being used directly here while it shouldn't be... diff --git a/src/sp-conn-end.cpp b/src/sp-conn-end.cpp index 3e5398ced..fa5a57529 100644 --- a/src/sp-conn-end.cpp +++ b/src/sp-conn-end.cpp @@ -50,8 +50,9 @@ static bool try_get_intersect_point_with_item_recursive(Geom::PathVector& conn_p // consider all first-order children double child_pos = 0.0; - for (GSList const* i = sp_item_group_item_list(group); i != NULL; i = i->next) { - SPItem* child_item = SP_ITEM(i->data); + std::vector<SPItem*> g = sp_item_group_item_list(group); + for (std::vector<SPItem*>::const_iterator i = g.begin();i!=g.end();i++) { + SPItem* child_item = *i; try_get_intersect_point_with_item_recursive(conn_pv, child_item, item_transform * child_item->transform, child_pos); if (intersect_pos < child_pos) @@ -109,7 +110,7 @@ static bool try_get_intersect_point_with_item(SPPath* conn, SPItem* item, if (!at_start) { // connectors are actually a single path, so consider the first element from a Geom::PathVector - conn_pv[0] = conn_pv[0].reverse(); + conn_pv[0] = conn_pv[0].reversed(); } // We start with the intersection point at the beginning of the path diff --git a/src/sp-defs.cpp b/src/sp-defs.cpp index 8dd695867..2e341d3c6 100644 --- a/src/sp-defs.cpp +++ b/src/sp-defs.cpp @@ -36,11 +36,9 @@ void SPDefs::update(SPCtx *ctx, guint flags) { } flags &= SP_OBJECT_MODIFIED_CASCADE; - - GSList *l = g_slist_reverse(this->childList(true)); - while (l) { - SPObject *child = SP_OBJECT(l->data); - l = g_slist_remove(l, child); + std::vector<SPObject*> l(this->childList(true)); + for(std::vector<SPObject*>::const_iterator i=l.begin();i!=l.end();i++){ + SPObject *child = *i; if (flags || (child->uflags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG))) { child->updateDisplay(ctx, flags); } diff --git a/src/sp-ellipse.cpp b/src/sp-ellipse.cpp index 932a3a1b7..0675b7781 100644 --- a/src/sp-ellipse.cpp +++ b/src/sp-ellipse.cpp @@ -18,6 +18,7 @@ #include <glibmm/i18n.h> #include <2geom/angle.h> +#include <2geom/circle.h> #include <2geom/ellipse.h> #include <2geom/path-sink.h> #include <2geom/pathvector.h> diff --git a/src/sp-factory.cpp b/src/sp-factory.cpp index e48646f8d..55e673c4a 100644 --- a/src/sp-factory.cpp +++ b/src/sp-factory.cpp @@ -27,6 +27,7 @@ #include "sp-font.h" #include "sp-font-face.h" #include "sp-glyph.h" +#include "sp-glyph-kerning.h" #include "sp-guide.h" #include "sp-hatch.h" #include "sp-hatch-path.h" @@ -150,6 +151,10 @@ SPObject *SPFactory::createObject(std::string const& id) ret = new SPFontFace; else if (id == "svg:glyph") ret = new SPGlyph; + else if (id == "svg:hkern") + ret = new SPHkern; + else if (id == "svg:vkern") + ret = new SPVkern; else if (id == "sodipodi:guide") ret = new SPGuide; else if (id == "svg:hatch") diff --git a/src/sp-filter-reference.cpp b/src/sp-filter-reference.cpp index 57600ad39..afb014820 100644 --- a/src/sp-filter-reference.cpp +++ b/src/sp-filter-reference.cpp @@ -4,7 +4,7 @@ bool SPFilterReference::_acceptObject(SPObject *obj) const { - return SP_IS_FILTER(obj); + return SP_IS_FILTER(obj) && URIReference::_acceptObject(obj); /* effic: Don't bother making this an inline function: _acceptObject is a virtual function, typically called from a context where the runtime type is not known at compile time. */ } diff --git a/src/sp-filter.cpp b/src/sp-filter.cpp index a70fbd5bb..1bde69dd1 100644 --- a/src/sp-filter.cpp +++ b/src/sp-filter.cpp @@ -79,6 +79,7 @@ void SPFilter::build(SPDocument *document, Inkscape::XML::Node *repr) { this->readAttr( "height" ); this->readAttr( "filterRes" ); this->readAttr( "xlink:href" ); + this->_refcount = 0; SPObject::build(document, repr); @@ -190,6 +191,15 @@ void SPFilter::set(unsigned int key, gchar const *value) { } } + +/** + * Returns the number of references to the filter. + */ +guint SPFilter::getRefCount() { + // NOTE: this is currently updated by sp_style_filter_ref_changed() in style.cpp + return _refcount; +} + /** * Receives update notifications. */ @@ -235,11 +245,9 @@ void SPFilter::update(SPCtx *ctx, guint flags) { childflags |= SP_OBJECT_PARENT_MODIFIED_FLAG; } childflags &= SP_OBJECT_MODIFIED_CASCADE; - - GSList *l = g_slist_reverse(this->childList(true, SPObject::ActionUpdate)); - while (l) { - SPObject *child = SP_OBJECT (l->data); - l = g_slist_remove (l, child); + std::vector<SPObject*> l(this->childList(true, SPObject::ActionUpdate)); + for(std::vector<SPObject*>::const_iterator i=l.begin();i!=l.end();i++){ + SPObject *child = *i; if( SP_IS_FILTER_PRIMITIVE( child ) ) { child->updateDisplay(ctx, childflags); } diff --git a/src/sp-filter.h b/src/sp-filter.h index e6318c569..1c214c6b7 100644 --- a/src/sp-filter.h +++ b/src/sp-filter.h @@ -54,6 +54,9 @@ public: NumberOptNumber filterRes; SPFilterReference *href; sigc::connection modified_connection; + + guint getRefCount(); + guint _refcount; Inkscape::Filters::Filter *_renderer; diff --git a/src/sp-gradient-reference.cpp b/src/sp-gradient-reference.cpp index d2b8128fb..216ac73de 100644 --- a/src/sp-gradient-reference.cpp +++ b/src/sp-gradient-reference.cpp @@ -4,7 +4,7 @@ bool SPGradientReference::_acceptObject(SPObject *obj) const { - return SP_IS_GRADIENT(obj); + return SP_IS_GRADIENT(obj) && URIReference::_acceptObject(obj); /* effic: Don't bother making this an inline function: _acceptObject is a virtual function, typically called from a context where the runtime type is not known at compile time. */ } diff --git a/src/sp-gradient-test.h b/src/sp-gradient-test.h index 696072929..578d0c5c0 100644 --- a/src/sp-gradient-test.h +++ b/src/sp-gradient-test.h @@ -102,10 +102,10 @@ public: Geom::Affine const g2d(sp_gradient_get_g2d_matrix(gr, Geom::identity(), unit_rect)); Geom::Affine const gs2d(sp_gradient_get_gs2d_matrix(gr, Geom::identity(), unit_rect)); TS_ASSERT_EQUALS( g2d, Geom::identity() ); - TS_ASSERT( Geom::matrix_equalp(gs2d, gr->gradientTransform * g2d, 1e-12) ); + TS_ASSERT( Geom::are_near(gs2d, gr->gradientTransform * g2d, 1e-12) ); sp_gradient_set_gs2d_matrix(gr, Geom::identity(), unit_rect, gs2d); - TS_ASSERT( Geom::matrix_equalp(gr->gradientTransform, grXform, 1e-12) ); + TS_ASSERT( Geom::are_near(gr->gradientTransform, grXform, 1e-12) ); } gr->gradientTransform = grXform; @@ -116,10 +116,10 @@ public: Geom::Affine const g2d(sp_gradient_get_g2d_matrix(gr, funny, unit_rect)); Geom::Affine const gs2d(sp_gradient_get_gs2d_matrix(gr, funny, unit_rect)); TS_ASSERT_EQUALS( g2d, funny ); - TS_ASSERT( Geom::matrix_equalp(gs2d, gr->gradientTransform * g2d, 1e-12) ); + TS_ASSERT( Geom::are_near(gs2d, gr->gradientTransform * g2d, 1e-12) ); sp_gradient_set_gs2d_matrix(gr, funny, unit_rect, gs2d); - TS_ASSERT( Geom::matrix_equalp(gr->gradientTransform, grXform, 1e-12) ); + TS_ASSERT( Geom::are_near(gr->gradientTransform, grXform, 1e-12) ); } gr->gradientTransform = grXform; @@ -130,16 +130,16 @@ public: TS_ASSERT_EQUALS( g2d, Geom::Affine(3, 0, 0, 4, 5, 6) * funny ); - TS_ASSERT( Geom::matrix_equalp(gs2d, gr->gradientTransform * g2d, 1e-12) ); + TS_ASSERT( Geom::are_near(gs2d, gr->gradientTransform * g2d, 1e-12) ); sp_gradient_set_gs2d_matrix(gr, funny, larger_rect, gs2d); - TS_ASSERT( Geom::matrix_equalp(gr->gradientTransform, grXform, 1e-12) ); + TS_ASSERT( Geom::are_near(gr->gradientTransform, grXform, 1e-12) ); SP_OBJECT(gr)->setKeyValue( SP_ATTR_GRADIENTUNITS, "userSpaceOnUse"); Geom::Affine const user_g2d(sp_gradient_get_g2d_matrix(gr, funny, larger_rect)); Geom::Affine const user_gs2d(sp_gradient_get_gs2d_matrix(gr, funny, larger_rect)); TS_ASSERT_EQUALS( user_g2d, funny ); - TS_ASSERT( Geom::matrix_equalp(user_gs2d, gr->gradientTransform * user_g2d, 1e-12) ); + TS_ASSERT( Geom::are_near(user_gs2d, gr->gradientTransform * user_g2d, 1e-12) ); } g_object_unref(gr); } diff --git a/src/sp-hatch.h b/src/sp-hatch.h index 5004a611f..546f06a1e 100644 --- a/src/sp-hatch.h +++ b/src/sp-hatch.h @@ -168,7 +168,7 @@ public: protected: virtual bool _acceptObject(SPObject *obj) const { - return dynamic_cast<SPHatch *>(obj) != NULL; + return dynamic_cast<SPHatch *>(obj) != NULL && URIReference::_acceptObject(obj); } }; diff --git a/src/sp-item-group.cpp b/src/sp-item-group.cpp index 55857dacf..72cd5d7fa 100644 --- a/src/sp-item-group.cpp +++ b/src/sp-item-group.cpp @@ -161,11 +161,9 @@ void SPGroup::update(SPCtx *ctx, unsigned int flags) { childflags |= SP_OBJECT_PARENT_MODIFIED_FLAG; } childflags &= SP_OBJECT_MODIFIED_CASCADE; - - GSList *l = g_slist_reverse(this->childList(true, SPObject::ActionUpdate)); - while (l) { - SPObject *child = SP_OBJECT (l->data); - l = g_slist_remove (l, child); + std::vector<SPObject*> l=this->childList(true, SPObject::ActionUpdate); + for(std::vector<SPObject*> ::const_iterator i=l.begin();i!=l.end();i++){ + SPObject *child = *i; if (childflags || (child->uflags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG))) { SPItem *item = dynamic_cast<SPItem *>(child); @@ -201,20 +199,15 @@ void SPGroup::update(SPCtx *ctx, unsigned int flags) { void SPGroup::modified(guint flags) { // std::cout << "SPGroup::modified(): " << (getId()?getId():"null") << std::endl; SPLPEItem::modified(flags); - - SPObject *child; - if (flags & SP_OBJECT_MODIFIED_FLAG) { flags |= SP_OBJECT_PARENT_MODIFIED_FLAG; } flags &= SP_OBJECT_MODIFIED_CASCADE; - GSList *l = g_slist_reverse(this->childList(true)); - - while (l) { - child = SP_OBJECT (l->data); - l = g_slist_remove (l, child); + std::vector<SPObject*> l=this->childList(true); + for(std::vector<SPObject*>::const_iterator i=l.begin();i!=l.end();i++){ + SPObject *child = *i; if (flags || (child->mflags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG))) { child->emitModified(flags); @@ -286,35 +279,27 @@ Geom::OptRect SPGroup::bbox(Geom::Affine const &transform, SPItem::BBoxType bbox Geom::OptRect bbox; // TODO CPPIFY: replace this const_cast later - GSList *l = const_cast<SPGroup*>(this)->childList(false, SPObject::ActionBBox); - - while (l) { - SPObject *o = SP_OBJECT (l->data); - + std::vector<SPObject*> l = const_cast<SPGroup*>(this)->childList(false, SPObject::ActionBBox); + for(std::vector<SPObject*>::const_iterator i=l.begin();i!=l.end();i++){ + SPObject *o = *i; SPItem *item = dynamic_cast<SPItem *>(o); if (item && !item->isHidden()) { Geom::Affine const ct(item->transform * transform); bbox |= item->bounds(bboxtype, ct); } - - l = g_slist_remove (l, o); } return bbox; } void SPGroup::print(SPPrintContext *ctx) { - GSList *l = g_slist_reverse(this->childList(false)); - - while (l) { - SPObject *o = SP_OBJECT (l->data); - + std::vector<SPObject*> l=this->childList(false); + for(std::vector<SPObject*>::const_iterator i=l.begin();i!=l.end();i++){ + SPObject *o = *i; SPItem *item = dynamic_cast<SPItem *>(o); if (item) { item->invoke_print(ctx); } - - l = g_slist_remove (l, o); } } @@ -362,17 +347,14 @@ Inkscape::DrawingItem *SPGroup::show (Inkscape::Drawing &drawing, unsigned int k } void SPGroup::hide (unsigned int key) { - GSList *l = g_slist_reverse(this->childList(false, SPObject::ActionShow)); - - while (l) { - SPObject *o = SP_OBJECT (l->data); + std::vector<SPObject*> l=this->childList(false, SPObject::ActionShow); + for(std::vector<SPObject*>::const_iterator i=l.begin();i!=l.end();i++){ + SPObject *o = *i; SPItem *item = dynamic_cast<SPItem *>(o); if (item) { item->invoke_hide(key); } - - l = g_slist_remove (l, o); } // SPLPEItem::onHide(key); @@ -393,7 +375,7 @@ void sp_item_group_ungroup_handle_clones(SPItem *parent, Geom::Affine const g) { for(std::list<SPObject*>::const_iterator refd=parent->hrefList.begin();refd!=parent->hrefList.end();refd++){ SPItem *citem = dynamic_cast<SPItem *>(*refd); - if (citem) { + if (citem && !citem->cloned) { SPUse *useitem = dynamic_cast<SPUse *>(citem); if (useitem && useitem->get_original() == parent) { Geom::Affine ctrans; @@ -407,7 +389,7 @@ void sp_item_group_ungroup_handle_clones(SPItem *parent, Geom::Affine const g) } void -sp_item_group_ungroup (SPGroup *group, GSList **children, bool do_done) +sp_item_group_ungroup (SPGroup *group, std::vector<SPItem*> &children, bool do_done) { g_return_if_fail (group != NULL); @@ -559,15 +541,12 @@ sp_item_group_ungroup (SPGroup *group, GSList **children, bool do_done) if (item) { item->doWriteTransform(repr, item->transform, NULL, false); + children.insert(children.begin(),item); } else { g_assert_not_reached(); } Inkscape::GC::release(repr); - if (children && item) { - *children = g_slist_prepend(*children, item); - } - items = g_slist_remove (items, items->data); } @@ -580,19 +559,17 @@ sp_item_group_ungroup (SPGroup *group, GSList **children, bool do_done) * some API for list aspect of SPGroup */ -GSList *sp_item_group_item_list(SPGroup * group) +std::vector<SPItem*> sp_item_group_item_list(SPGroup * group) { - g_return_val_if_fail(group != NULL, NULL); - - GSList *s = NULL; + std::vector<SPItem*> s; + g_return_val_if_fail(group != NULL, s); for (SPObject *o = group->firstChild() ; o ; o = o->getNext() ) { if ( dynamic_cast<SPItem *>(o) ) { - s = g_slist_prepend(s, o); + s.push_back((SPItem*)o); } } - - return g_slist_reverse (s); + return s; } SPObject *sp_item_group_get_child_by_name(SPGroup *group, SPObject *ref, const gchar *name) @@ -807,9 +784,9 @@ gint SPGroup::getItemCount() const { void SPGroup::_showChildren (Inkscape::Drawing &drawing, Inkscape::DrawingItem *ai, unsigned int key, unsigned int flags) { Inkscape::DrawingItem *ac = NULL; - GSList *l = g_slist_reverse(this->childList(false, SPObject::ActionShow)); - while (l) { - SPObject *o = SP_OBJECT (l->data); + std::vector<SPObject*> l=this->childList(false, SPObject::ActionShow); + for(std::vector<SPObject*>::const_iterator i=l.begin();i!=l.end();i++){ + SPObject *o = *i; SPItem * child = dynamic_cast<SPItem *>(o); if (child) { ac = child->invoke_show (drawing, key, flags); @@ -817,7 +794,6 @@ void SPGroup::_showChildren (Inkscape::Drawing &drawing, Inkscape::DrawingItem * ai->appendChild(ac); } } - l = g_slist_remove (l, o); } } @@ -826,10 +802,10 @@ void SPGroup::update_patheffect(bool write) { g_message("sp_group_update_patheffect: %p\n", lpeitem); #endif - GSList const *item_list = sp_item_group_item_list(this); + std::vector<SPItem*> const item_list = sp_item_group_item_list(this); - for ( GSList const *iter = item_list; iter; iter = iter->next ) { - SPObject *subitem = static_cast<SPObject *>(iter->data); + for ( std::vector<SPItem*>::const_iterator iter=item_list.begin();iter!=item_list.end();iter++) { + SPObject *subitem = *iter; SPLPEItem *lpeItem = dynamic_cast<SPLPEItem *>(subitem); if (lpeItem) { @@ -854,10 +830,10 @@ void SPGroup::update_patheffect(bool write) { static void sp_group_perform_patheffect(SPGroup *group, SPGroup *topgroup, bool write) { - GSList const *item_list = sp_item_group_item_list(group); + std::vector<SPItem*> const item_list = sp_item_group_item_list(group); - for ( GSList const *iter = item_list; iter; iter = iter->next ) { - SPObject *subitem = static_cast<SPObject *>(iter->data); + for ( std::vector<SPItem*>::const_iterator iter=item_list.begin();iter!=item_list.end();iter++) { + SPObject *subitem = *iter; SPGroup *subGroup = dynamic_cast<SPGroup *>(subitem); if (subGroup) { diff --git a/src/sp-item-group.h b/src/sp-item-group.h index f9a8b88f5..fc0b6382f 100644 --- a/src/sp-item-group.h +++ b/src/sp-item-group.h @@ -95,6 +95,7 @@ public: virtual void update_patheffect(bool write); }; + /** * finds clones of a child of the group going out of the group; and inverse the group transform on its clones * Also called when moving objects between different layers @@ -104,10 +105,11 @@ public: */ void sp_item_group_ungroup_handle_clones(SPItem *parent, Geom::Affine const g); -void sp_item_group_ungroup (SPGroup *group, GSList **children, bool do_done = true); +void sp_item_group_ungroup (SPGroup *group, std::vector<SPItem*> &children, bool do_done = true); + +std::vector<SPItem*> sp_item_group_item_list (SPGroup *group); -GSList *sp_item_group_item_list (SPGroup *group); SPObject *sp_item_group_get_child_by_name (SPGroup *group, SPObject *ref, const char *name); #endif diff --git a/src/sp-item-transform.cpp b/src/sp-item-transform.cpp index 767f0ed91..f1c69cdf6 100644 --- a/src/sp-item-transform.cpp +++ b/src/sp-item-transform.cpp @@ -152,7 +152,7 @@ Geom::Affine get_scale_transform_for_uniform_stroke(Geom::Rect const &bbox_visua gdouble scale_x = 1; gdouble scale_y = 1; - gdouble r1 = r0; + gdouble r1; if ((fabs(w0 - stroke_x) < 1e-6) || w1 == 0) { // We have a vertical line at hand scale_y = h1/h0; @@ -310,8 +310,8 @@ Geom::Affine get_scale_transform_for_variable_stroke(Geom::Rect const &bbox_visu gdouble scale_x = 1; gdouble scale_y = 1; - gdouble r1h = r0h; - gdouble r1w = r0w; + gdouble r1h; + gdouble r1w; if ((fabs(w0 - r0w) < 1e-6) || w1 == 0) { // We have a vertical line at hand scale_y = h1/h0; diff --git a/src/sp-item.cpp b/src/sp-item.cpp index 8c99e9bcf..410fd9b37 100644 --- a/src/sp-item.cpp +++ b/src/sp-item.cpp @@ -349,17 +349,11 @@ void SPItem::lowerToBottom() { using Inkscape::Util::MutableList; using Inkscape::Util::reverse_list; - MutableList<SPObject &> bottom=find_last_if( - reverse_list<SPObject::SiblingIterator>( - parent->firstChild(), this - ), - MutableList<SPObject &>(), - &is_item - ); + SPObject * bottom=parent->firstChild(); + while(dynamic_cast<SPObject*>(bottom) && dynamic_cast<SPObject*>(bottom->next) && bottom!=this && !is_item(*(bottom->next))) bottom=bottom->next; if (bottom) { - ++bottom; Inkscape::XML::Node *ref = ( bottom ? bottom->getRepr() : NULL ); - getRepr()->parent()->changeOrder(getRepr(), ref); + parent->getRepr()->changeOrder(getRepr(), ref); } } @@ -367,20 +361,20 @@ void SPItem::moveTo(SPItem *target, bool intoafter) { Inkscape::XML::Node *target_ref = ( target ? target->getRepr() : NULL ); Inkscape::XML::Node *our_ref = getRepr(); - gboolean first = FALSE; - - if (target_ref == our_ref) { - // Move to ourself ignore - return; - } if (!target_ref) { // Assume move to the "first" in the top node, find the top node - target_ref = our_ref; - while (target_ref->parent() != target_ref->root()) { - target_ref = target_ref->parent(); + intoafter = false; + SPObject* bottom = this->document->getObjectByRepr(our_ref->root())->firstChild(); + while(!dynamic_cast<SPItem*>(bottom->next)){ + bottom=bottom->next; } - first = TRUE; + target_ref = bottom->getRepr(); + } + + if (target_ref == our_ref) { + // Move to ourself ignore + return; } if (intoafter) { @@ -391,16 +385,10 @@ void SPItem::moveTo(SPItem *target, bool intoafter) { // Change in parent, need to remove and add our_ref->parent()->removeChild(our_ref); target_ref->parent()->addChild(our_ref, target_ref); - } else if (!first) { + } else { // Same parent, just move our_ref->parent()->changeOrder(our_ref, target_ref); } - - if (first && parent) { - // If "first" ensure it appears after the defs etc - lowerToBottom(); - return; - } } void SPItem::build(SPDocument *document, Inkscape::XML::Node *repr) { @@ -1234,8 +1222,8 @@ void SPItem::adjust_pattern(Geom::Affine const &postmul, bool set, PatternTransf SPObject *server = style->getFillPaintServer(); SPPattern *serverPatt = dynamic_cast<SPPattern *>(server); if ( serverPatt ) { - SPPattern *pattern = sp_pattern_clone_if_necessary(this, serverPatt, "fill"); - sp_pattern_transform_multiply(pattern, postmul, set); + SPPattern *pattern = serverPatt->clone_if_necessary(this, "fill"); + pattern->transform_multiply(postmul, set); } } @@ -1244,8 +1232,8 @@ void SPItem::adjust_pattern(Geom::Affine const &postmul, bool set, PatternTransf SPObject *server = style->getStrokePaintServer(); SPPattern *serverPatt = dynamic_cast<SPPattern *>(server); if ( serverPatt ) { - SPPattern *pattern = sp_pattern_clone_if_necessary(this, serverPatt, "stroke"); - sp_pattern_transform_multiply(pattern, postmul, set); + SPPattern *pattern = serverPatt->clone_if_necessary(this, "stroke"); + pattern->transform_multiply(postmul, set); } } } diff --git a/src/sp-lpe-item.cpp b/src/sp-lpe-item.cpp index 12d85b3ed..9befd2a2b 100644 --- a/src/sp-lpe-item.cpp +++ b/src/sp-lpe-item.cpp @@ -353,9 +353,9 @@ sp_lpe_item_create_original_path_recursive(SPLPEItem *lpeitem) sp_lpe_item_create_original_path_recursive(SP_LPE_ITEM(clipPath->firstChild())); } if (SP_IS_GROUP(lpeitem)) { - GSList const *item_list = sp_item_group_item_list(SP_GROUP(lpeitem)); - for ( GSList const *iter = item_list; iter; iter = iter->next ) { - SPObject *subitem = static_cast<SPObject *>(iter->data); + std::vector<SPItem*> item_list = sp_item_group_item_list(SP_GROUP(lpeitem)); + for ( std::vector<SPItem*>::const_iterator iter=item_list.begin();iter!=item_list.end();iter++) { + SPObject *subitem = *iter; if (SP_IS_LPE_ITEM(subitem)) { sp_lpe_item_create_original_path_recursive(SP_LPE_ITEM(subitem)); } @@ -387,9 +387,9 @@ sp_lpe_item_cleanup_original_path_recursive(SPLPEItem *lpeitem) sp_lpe_item_cleanup_original_path_recursive(SP_LPE_ITEM(clipPath->firstChild())); } } - GSList const *item_list = sp_item_group_item_list(SP_GROUP(lpeitem)); - for ( GSList const *iter = item_list; iter; iter = iter->next ) { - SPObject *subitem = static_cast<SPObject *>(iter->data); + std::vector<SPItem*> item_list = sp_item_group_item_list(SP_GROUP(lpeitem)); + for ( std::vector<SPItem*>::const_iterator iter=item_list.begin();iter!=item_list.end();iter++) { + SPObject *subitem = *iter; if (SP_IS_LPE_ITEM(subitem)) { sp_lpe_item_cleanup_original_path_recursive(SP_LPE_ITEM(subitem)); } @@ -680,9 +680,9 @@ SPLPEItem::apply_to_clippath(SPItem *item) } } if(SP_IS_GROUP(item)){ - GSList const *item_list = sp_item_group_item_list(SP_GROUP(item)); - for ( GSList const *iter = item_list; iter; iter = iter->next ) { - SPObject *subitem = static_cast<SPObject *>(iter->data); + std::vector<SPItem*> item_list = sp_item_group_item_list(SP_GROUP(item)); + for ( std::vector<SPItem*>::const_iterator iter=item_list.begin();iter!=item_list.end();iter++) { + SPObject *subitem = *iter; apply_to_clippath(SP_ITEM(subitem)); } } @@ -732,9 +732,9 @@ SPLPEItem::apply_to_mask(SPItem *item) } } if(SP_IS_GROUP(item)){ - GSList const *item_list = sp_item_group_item_list(SP_GROUP(item)); - for ( GSList const *iter = item_list; iter; iter = iter->next ) { - SPObject *subitem = static_cast<SPObject *>(iter->data); + std::vector<SPItem*> item_list = sp_item_group_item_list(SP_GROUP(item)); + for ( std::vector<SPItem*>::const_iterator iter=item_list.begin();iter!=item_list.end();iter++) { + SPObject *subitem = *iter; apply_to_mask(SP_ITEM(subitem)); } } @@ -746,9 +746,9 @@ SPLPEItem::apply_to_clip_or_mask_group(SPItem *group, SPItem *item) if (!SP_IS_GROUP(group)) { return; } - GSList *item_list = sp_item_group_item_list(SP_GROUP(group)); - for ( GSList *iter = item_list; iter; iter = iter->next ) { - SPObject *subitem = static_cast<SPObject *>(iter->data); + std::vector<SPItem*> item_list = sp_item_group_item_list(SP_GROUP(group)); + for ( std::vector<SPItem*>::const_iterator iter=item_list.begin();iter!=item_list.end();iter++) { + SPObject *subitem = *iter; if (SP_IS_GROUP(subitem)) { apply_to_clip_or_mask_group(SP_ITEM(subitem), item); } else if (SP_IS_SHAPE(subitem)) { diff --git a/src/sp-marker.cpp b/src/sp-marker.cpp index d3cf50fa7..9334614dc 100644 --- a/src/sp-marker.cpp +++ b/src/sp-marker.cpp @@ -419,7 +419,7 @@ sp_marker_hide (SPMarker *marker, unsigned int key) } -const gchar *generate_marker(GSList *reprs, Geom::Rect bounds, SPDocument *document, Geom::Point center, Geom::Affine move) +const gchar *generate_marker(std::vector<Inkscape::XML::Node*> &reprs, Geom::Rect bounds, SPDocument *document, Geom::Point center, Geom::Affine move) { Inkscape::XML::Document *xml_doc = document->getReprDoc(); Inkscape::XML::Node *defsrepr = document->getDefs()->getRepr(); @@ -442,8 +442,8 @@ const gchar *generate_marker(GSList *reprs, Geom::Rect bounds, SPDocument *docum const gchar *mark_id = repr->attribute("id"); SPObject *mark_object = document->getObjectById(mark_id); - for (GSList *i = reprs; i != NULL; i = i->next) { - Inkscape::XML::Node *node = (Inkscape::XML::Node *)(i->data); + for (std::vector<Inkscape::XML::Node*>::const_iterator i=reprs.begin();i!=reprs.end();i++){ + Inkscape::XML::Node *node = *i; SPItem *copy = SP_ITEM(mark_object->appendChildRepr(node)); Geom::Affine dup_transform; diff --git a/src/sp-marker.h b/src/sp-marker.h index e804fd7dc..bae13243b 100644 --- a/src/sp-marker.h +++ b/src/sp-marker.h @@ -92,7 +92,7 @@ class SPMarkerReference : public Inkscape::URIReference { } protected: virtual bool _acceptObject(SPObject *obj) const { - return SP_IS_MARKER(obj); + return SP_IS_MARKER(obj) && URIReference::_acceptObject(obj); } }; @@ -101,7 +101,7 @@ Inkscape::DrawingItem *sp_marker_show_instance (SPMarker *marker, Inkscape::Draw unsigned int key, unsigned int pos, Geom::Affine const &base, float linewidth); void sp_marker_hide (SPMarker *marker, unsigned int key); -const char *generate_marker (GSList *reprs, Geom::Rect bounds, SPDocument *document, Geom::Point center, Geom::Affine move); +const char *generate_marker (std::vector<Inkscape::XML::Node*> &reprs, Geom::Rect bounds, SPDocument *document, Geom::Point center, Geom::Affine move); SPObject *sp_marker_fork_if_necessary(SPObject *marker); #endif diff --git a/src/sp-mask.cpp b/src/sp-mask.cpp index d60473e1d..f8fb7aff4 100644 --- a/src/sp-mask.cpp +++ b/src/sp-mask.cpp @@ -209,7 +209,7 @@ Inkscape::XML::Node* SPMask::write(Inkscape::XML::Document* xml_doc, Inkscape::X // Create a mask element (using passed elements), add it to <defs> const gchar * -sp_mask_create (GSList *reprs, SPDocument *document, Geom::Affine const* applyTransform) +sp_mask_create (std::vector<Inkscape::XML::Node*> &reprs, SPDocument *document, Geom::Affine const* applyTransform) { Inkscape::XML::Node *defsrepr = document->getDefs()->getRepr(); @@ -221,13 +221,12 @@ sp_mask_create (GSList *reprs, SPDocument *document, Geom::Affine const* applyTr const gchar *mask_id = repr->attribute("id"); SPObject *mask_object = document->getObjectById(mask_id); - for (GSList *it = reprs; it != NULL; it = it->next) { - Inkscape::XML::Node *node = (Inkscape::XML::Node *)(it->data); + for (std::vector<Inkscape::XML::Node*>::const_iterator it = reprs.begin(); it != reprs.end(); it++) { + Inkscape::XML::Node *node = (*it); SPItem *item = SP_ITEM(mask_object->appendChildRepr(node)); if (NULL != applyTransform) { - Geom::Affine transform (item->transform); - transform *= (*applyTransform); + Geom::Affine transform (item->transform * (*applyTransform)); item->doWriteTransform(item->getRepr(), transform); } } diff --git a/src/sp-mask.h b/src/sp-mask.h index e991fedb6..74bd4d66e 100644 --- a/src/sp-mask.h +++ b/src/sp-mask.h @@ -81,7 +81,7 @@ protected: return false; } SPObject * const owner = this->getOwner(); - if (obj->isAncestorOf(owner)) { + if (!URIReference::_acceptObject(obj)) { //XML Tree being used directly here while it shouldn't be... Inkscape::XML::Node * const owner_repr = owner->getRepr(); //XML Tree being used directly here while it shouldn't be... @@ -108,6 +108,6 @@ protected: } }; -const char *sp_mask_create (GSList *reprs, SPDocument *document, Geom::Affine const* applyTransform); +const char *sp_mask_create (std::vector<Inkscape::XML::Node*> &reprs, SPDocument *document, Geom::Affine const* applyTransform); #endif // SEEN_SP_MASK_H diff --git a/src/sp-namedview.cpp b/src/sp-namedview.cpp index 3154c9e7b..12c2cdf8e 100644 --- a/src/sp-namedview.cpp +++ b/src/sp-namedview.cpp @@ -517,7 +517,7 @@ void SPNamedView::set(unsigned int key, const gchar* value) { this->requestModified(SP_OBJECT_MODIFIED_FLAG); break; case SP_ATTR_INKSCAPE_SNAP_NODE_CUSP: - this->snap_manager.snapprefs.setTargetSnappable(Inkscape::SNAPTARGET_NODE_CUSP, value ? sp_str_to_bool(value) : FALSE); + this->snap_manager.snapprefs.setTargetSnappable(Inkscape::SNAPTARGET_NODE_CUSP, value ? sp_str_to_bool(value) : TRUE); this->requestModified(SP_OBJECT_MODIFIED_FLAG); break; case SP_ATTR_INKSCAPE_SNAP_BBOX_EDGE: diff --git a/src/sp-object.cpp b/src/sp-object.cpp index 343db435e..db66eb3e6 100644 --- a/src/sp-object.cpp +++ b/src/sp-object.cpp @@ -357,6 +357,10 @@ int sp_object_compare_position(SPObject const *first, SPObject const *second) return result; } +bool sp_object_compare_position_bool(SPObject const *first, SPObject const *second){ + return sp_object_compare_position(first,second)<0; +} + SPObject *SPObject::appendChildRepr(Inkscape::XML::Node *repr) { if ( !cloned ) { @@ -380,14 +384,14 @@ void SPObject::changeCSS(SPCSSAttr *css, gchar const *attr) sp_repr_css_change(this->getRepr(), css, attr); } -GSList *SPObject::childList(bool add_ref, Action) { - GSList *l = NULL; +std::vector<SPObject*> SPObject::childList(bool add_ref, Action) { + std::vector<SPObject*> l; for ( SPObject *child = firstChild() ; child; child = child->getNext() ) { if (add_ref) { sp_object_ref (child); } - l = g_slist_prepend (l, child); + l.push_back(child); } return l; @@ -717,6 +721,9 @@ void SPObject::invoke_build(SPDocument *document, Inkscape::XML::Node *repr, uns } this->cloned = cloned; + /* Invoke derived methods, if any */ + this->build(document, repr); + if ( !cloned ) { this->document->bindObjectToRepr(this->repr, this); @@ -750,8 +757,6 @@ void SPObject::invoke_build(SPDocument *document, Inkscape::XML::Node *repr, uns g_assert(this->getId() == NULL); } - /* Invoke derived methods, if any */ - this->build(document, repr); /* Signalling (should be connected AFTER processing derived methods */ sp_repr_add_listener(repr, &object_event_vector, this); diff --git a/src/sp-object.h b/src/sp-object.h index ab0b444b6..7bc02fad5 100644 --- a/src/sp-object.h +++ b/src/sp-object.h @@ -52,6 +52,7 @@ class SPObject; #include <sigc++/connection.h> #include <sigc++/functors/slot.h> #include <sigc++/signal.h> +#include <vector> #include "version.h" #include "util/forward-pointer-iterator.h" @@ -328,10 +329,10 @@ public: enum Action { ActionGeneral, ActionBBox, ActionUpdate, ActionShow }; /** - * Retrieves the children as a GSList object, optionally ref'ing the children + * Retrieves the children as a std vector object, optionally ref'ing the children * in the process, if add_ref is specified. */ - GSList *childList(bool add_ref, Action action = ActionGeneral); + std::vector<SPObject*> childList(bool add_ref, Action action = ActionGeneral); /** * Append repr as child of this object. @@ -875,6 +876,7 @@ public: * -1 first object's position is less than the second \endverbatim */ int sp_object_compare_position(SPObject const *first, SPObject const *second); +bool sp_object_compare_position_bool(SPObject const *first, SPObject const *second); #endif // SP_OBJECT_H_SEEN diff --git a/src/sp-offset.cpp b/src/sp-offset.cpp index 15d3821c7..7c3d0bd03 100644 --- a/src/sp-offset.cpp +++ b/src/sp-offset.cpp @@ -83,7 +83,7 @@ static void sp_offset_source_modified (SPObject *iSource, guint flags, SPItem *i // fast is not mathematically correct, because computing the offset of a single // cubic bezier patch is not trivial; in particular, there are problems with holes // reappearing in offset when the radius becomes too large -static bool use_slow_but_correct_offset_method=false; +static bool use_slow_but_correct_offset_method=true; SPOffset::SPOffset() : SPShape() { this->rad = 1.0; @@ -890,7 +890,7 @@ sp_offset_distance_to_original (SPOffset * offset, Geom::Point px) if (ab > 0 && ab < len * len) { // we're in the zone of influence of the segment - double ndist = (cross(pxsx,nx)) / len; + double ndist = (cross(nx, pxsx)) / len; if (arSet == false || fabs (ndist) < fabs (arDist)) { diff --git a/src/sp-paint-server.cpp b/src/sp-paint-server.cpp index ae40ad1aa..c3416c6c8 100644 --- a/src/sp-paint-server.cpp +++ b/src/sp-paint-server.cpp @@ -27,7 +27,7 @@ SPPaintServer *SPPaintServerReference::getObject() const bool SPPaintServerReference::_acceptObject(SPObject *obj) const { - return SP_IS_PAINT_SERVER(obj); + return SP_IS_PAINT_SERVER(obj) && URIReference::_acceptObject(obj); } SPPaintServer::SPPaintServer() : SPObject() { diff --git a/src/sp-path.cpp b/src/sp-path.cpp index 42883588b..cabed0467 100644 --- a/src/sp-path.cpp +++ b/src/sp-path.cpp @@ -28,8 +28,7 @@ #include "display/curve.h" #include <2geom/pathvector.h> -#include <2geom/bezier-curve.h> -#include <2geom/hvlinesegment.h> +#include <2geom/curves.h> #include "helper/geom-curves.h" #include "svg/svg.h" diff --git a/src/sp-pattern.cpp b/src/sp-pattern.cpp index 987a86fe4..755d3d162 100644 --- a/src/sp-pattern.cpp +++ b/src/sp-pattern.cpp @@ -5,7 +5,7 @@ * Lauris Kaplinski <lauris@kaplinski.com> * bulia byak <buliabyak@users.sf.net> * Jon A. Cruz <jon@joncruz.org> - * Abhishek Sharma + * Abhishek Sharma * * Copyright (C) 2002 Lauris Kaplinski * @@ -13,15 +13,15 @@ */ #ifdef HAVE_CONFIG_H -# include "config.h" +#include "config.h" #endif #include <cstring> #include <string> #include <glibmm.h> #include <2geom/transforms.h> +#include <sigc++/functors/mem_fun.h> -#include "macros.h" #include "svg/svg.h" #include "display/cairo-utils.h" #include "display/drawing-context.h" @@ -34,67 +34,61 @@ #include "style.h" #include "sp-pattern.h" #include "xml/repr.h" -#include "display/grayscale.h" -#include <sigc++/functors/ptr_fun.h> -#include <sigc++/adaptors/bind.h> +#include "sp-factory.h" -/* - * Pattern - */ -static void pattern_ref_changed(SPObject *old_ref, SPObject *ref, SPPattern *pat); -static void pattern_ref_modified (SPObject *ref, guint flags, SPPattern *pattern); - -SPPattern::SPPattern() : SPPaintServer(), SPViewBox() { - this->href = NULL; - - this->ref = new SPPatternReference(this); - this->ref->changedSignal().connect(sigc::bind(sigc::ptr_fun(pattern_ref_changed), this)); +SPPattern::SPPattern() + : SPPaintServer() + , SPViewBox() +{ + this->ref = new SPPatternReference(this); + this->ref->changedSignal().connect(sigc::mem_fun(this, &SPPattern::_onRefChanged)); - this->patternUnits = SP_PATTERN_UNITS_OBJECTBOUNDINGBOX; - this->patternUnits_set = FALSE; + this->_pattern_units = UNITS_OBJECTBOUNDINGBOX; + this->_pattern_units_set = false; - this->patternContentUnits = SP_PATTERN_UNITS_USERSPACEONUSE; - this->patternContentUnits_set = FALSE; + this->_pattern_content_units = UNITS_USERSPACEONUSE; + this->_pattern_content_units_set = false; - this->patternTransform = Geom::identity(); - this->patternTransform_set = FALSE; + this->_pattern_transform = Geom::identity(); + this->_pattern_transform_set = false; - this->x.unset(); - this->y.unset(); - this->width.unset(); - this->height.unset(); + this->_x.unset(); + this->_y.unset(); + this->_width.unset(); + this->_height.unset(); } -SPPattern::~SPPattern() { -} +SPPattern::~SPPattern() {} -void SPPattern::build(SPDocument* doc, Inkscape::XML::Node* repr) { - SPPaintServer::build(doc, repr); - - this->readAttr( "patternUnits" ); - this->readAttr( "patternContentUnits" ); - this->readAttr( "patternTransform" ); - this->readAttr( "x" ); - this->readAttr( "y" ); - this->readAttr( "width" ); - this->readAttr( "height" ); - this->readAttr( "viewBox" ); - this->readAttr( "preserveAspectRatio" ); - this->readAttr( "xlink:href" ); - - /* Register ourselves */ - doc->addResource("pattern", this); +void SPPattern::build(SPDocument *doc, Inkscape::XML::Node *repr) +{ + SPPaintServer::build(doc, repr); + + this->readAttr("patternUnits"); + this->readAttr("patternContentUnits"); + this->readAttr("patternTransform"); + this->readAttr("x"); + this->readAttr("y"); + this->readAttr("width"); + this->readAttr("height"); + this->readAttr("viewBox"); + this->readAttr("preserveAspectRatio"); + this->readAttr("xlink:href"); + + /* Register ourselves */ + doc->addResource("pattern", this); } -void SPPattern::release() { +void SPPattern::release() +{ if (this->document) { // Unregister ourselves this->document->removeResource("pattern", this); } if (this->ref) { - this->modified_connection.disconnect(); + this->_modified_connection.disconnect(); this->ref->detach(); delete this->ref; this->ref = NULL; @@ -103,113 +97,121 @@ void SPPattern::release() { SPPaintServer::release(); } -void SPPattern::set(unsigned int key, const gchar* value) { - switch (key) { - case SP_ATTR_PATTERNUNITS: - if (value) { - if (!strcmp (value, "userSpaceOnUse")) { - this->patternUnits = SP_PATTERN_UNITS_USERSPACEONUSE; - } else { - this->patternUnits = SP_PATTERN_UNITS_OBJECTBOUNDINGBOX; - } - - this->patternUnits_set = TRUE; - } else { - this->patternUnits_set = FALSE; - } - - this->requestModified(SP_OBJECT_MODIFIED_FLAG); - break; - - case SP_ATTR_PATTERNCONTENTUNITS: - if (value) { - if (!strcmp (value, "userSpaceOnUse")) { - this->patternContentUnits = SP_PATTERN_UNITS_USERSPACEONUSE; - } else { - this->patternContentUnits = SP_PATTERN_UNITS_OBJECTBOUNDINGBOX; - } - - this->patternContentUnits_set = TRUE; - } else { - this->patternContentUnits_set = FALSE; - } - - this->requestModified(SP_OBJECT_MODIFIED_FLAG); - break; - - case SP_ATTR_PATTERNTRANSFORM: { - Geom::Affine t; - - if (value && sp_svg_transform_read (value, &t)) { - this->patternTransform = t; - this->patternTransform_set = TRUE; - } else { - this->patternTransform = Geom::identity(); - this->patternTransform_set = FALSE; - } - - this->requestModified(SP_OBJECT_MODIFIED_FLAG); - break; - } - case SP_ATTR_X: - this->x.readOrUnset(value); - this->requestModified(SP_OBJECT_MODIFIED_FLAG); - break; - - case SP_ATTR_Y: - this->y.readOrUnset(value); - this->requestModified(SP_OBJECT_MODIFIED_FLAG); - break; - - case SP_ATTR_WIDTH: - this->width.readOrUnset(value); - this->requestModified(SP_OBJECT_MODIFIED_FLAG); - break; - - case SP_ATTR_HEIGHT: - this->height.readOrUnset(value); - this->requestModified(SP_OBJECT_MODIFIED_FLAG); - break; - - case SP_ATTR_VIEWBOX: - set_viewBox( value ); +void SPPattern::set(unsigned int key, const gchar *value) +{ + switch (key) { + case SP_ATTR_PATTERNUNITS: + if (value) { + if (!strcmp(value, "userSpaceOnUse")) { + this->_pattern_units = UNITS_USERSPACEONUSE; + } + else { + this->_pattern_units = UNITS_OBJECTBOUNDINGBOX; + } + + this->_pattern_units_set = true; + } + else { + this->_pattern_units_set = false; + } + + this->requestModified(SP_OBJECT_MODIFIED_FLAG); + break; + + case SP_ATTR_PATTERNCONTENTUNITS: + if (value) { + if (!strcmp(value, "userSpaceOnUse")) { + this->_pattern_content_units = UNITS_USERSPACEONUSE; + } + else { + this->_pattern_content_units = UNITS_OBJECTBOUNDINGBOX; + } + + this->_pattern_content_units_set = true; + } + else { + this->_pattern_content_units_set = false; + } + + this->requestModified(SP_OBJECT_MODIFIED_FLAG); + break; + + case SP_ATTR_PATTERNTRANSFORM: { + Geom::Affine t; + + if (value && sp_svg_transform_read(value, &t)) { + this->_pattern_transform = t; + this->_pattern_transform_set = true; + } + else { + this->_pattern_transform = Geom::identity(); + this->_pattern_transform_set = false; + } + + this->requestModified(SP_OBJECT_MODIFIED_FLAG); + break; + } + case SP_ATTR_X: + this->_x.readOrUnset(value); + this->requestModified(SP_OBJECT_MODIFIED_FLAG); + break; + + case SP_ATTR_Y: + this->_y.readOrUnset(value); + this->requestModified(SP_OBJECT_MODIFIED_FLAG); + break; + + case SP_ATTR_WIDTH: + this->_width.readOrUnset(value); + this->requestModified(SP_OBJECT_MODIFIED_FLAG); + break; + + case SP_ATTR_HEIGHT: + this->_height.readOrUnset(value); + this->requestModified(SP_OBJECT_MODIFIED_FLAG); + break; + + case SP_ATTR_VIEWBOX: + set_viewBox(value); this->requestModified(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG); break; case SP_ATTR_PRESERVEASPECTRATIO: - set_preserveAspectRatio( value ); + set_preserveAspectRatio(value); this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG); break; - case SP_ATTR_XLINK_HREF: - if ( value && this->href && ( strcmp(value, this->href) == 0 ) ) { - /* Href unchanged, do nothing. */ - } else { - g_free(this->href); - this->href = NULL; - - if (value) { - // First, set the href field; it's only used in the "unchanged" check above. - this->href = g_strdup(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(); - } - } - } - break; - - default: - SPPaintServer::set(key, value); - 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(); + } + } + } + break; + + default: + SPPaintServer::set(key, value); + break; + } } @@ -217,104 +219,90 @@ void SPPattern::set(unsigned int key, const gchar* value) { /* fixme: We need ::order_changed handler too (Lauris) */ -static GSList *pattern_getchildren(SPPattern *pat) +void SPPattern::_getChildren(std::list<SPObject *> &l) { - GSList *l = NULL; - - for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) { + for (SPPattern *pat_i = this; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) { if (pat_i->firstChild()) { // find the first one with children - for (SPObject *child = pat->firstChild() ; child ; child = child->getNext() ) { - l = g_slist_prepend (l, child); - } - break; // do not go further up the chain if children are found + for (SPObject *child = pat_i->firstChild(); child; child = child->getNext()) { + l.push_back(child); + } + break; // do not go further up the chain if children are found } } - - return l; } -void SPPattern::update(SPCtx* ctx, unsigned int flags) { - if (flags & SP_OBJECT_MODIFIED_FLAG) { - flags |= SP_OBJECT_PARENT_MODIFIED_FLAG; - } +void SPPattern::update(SPCtx *ctx, unsigned int flags) +{ + typedef std::list<SPObject *>::iterator SPObjectIterator; + + if (flags & SP_OBJECT_MODIFIED_FLAG) { + flags |= SP_OBJECT_PARENT_MODIFIED_FLAG; + } - flags &= SP_OBJECT_MODIFIED_CASCADE; + flags &= SP_OBJECT_MODIFIED_CASCADE; - GSList *l = pattern_getchildren (this); - l = g_slist_reverse (l); + std::list<SPObject *> l; + _getChildren(l); - while (l) { - SPObject *child = SP_OBJECT (l->data); + for (SPObjectIterator it = l.begin(); it != l.end(); it++) { + SPObject *child = *it; - sp_object_ref (child, NULL); - l = g_slist_remove (l, child); + sp_object_ref(child, NULL); - if (flags || (child->mflags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG))) { - child->updateDisplay(ctx, flags); - } + if (flags || (child->mflags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG))) { + child->updateDisplay(ctx, flags); + } - sp_object_unref (child, NULL); - } + sp_object_unref(child, NULL); + } } -void SPPattern::modified(unsigned int flags) { - if (flags & SP_OBJECT_MODIFIED_FLAG) { - flags |= SP_OBJECT_PARENT_MODIFIED_FLAG; - } +void SPPattern::modified(unsigned int flags) +{ + typedef std::list<SPObject *>::iterator SPObjectIterator; + + if (flags & SP_OBJECT_MODIFIED_FLAG) { + flags |= SP_OBJECT_PARENT_MODIFIED_FLAG; + } - flags &= SP_OBJECT_MODIFIED_CASCADE; + flags &= SP_OBJECT_MODIFIED_CASCADE; - GSList *l = pattern_getchildren (this); - l = g_slist_reverse (l); + std::list<SPObject *> l; + _getChildren(l); - while (l) { - SPObject *child = SP_OBJECT (l->data); + for (SPObjectIterator it = l.begin(); it != l.end(); it++) { + SPObject *child = *it; - sp_object_ref (child, NULL); - l = g_slist_remove (l, child); + sp_object_ref(child, NULL); - if (flags || (child->mflags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG))) { - child->emitModified(flags); - } + if (flags || (child->mflags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG))) { + child->emitModified(flags); + } - sp_object_unref (child, NULL); - } + sp_object_unref(child, NULL); + } } -/** -Gets called when the pattern is reattached to another <pattern> -*/ -static void -pattern_ref_changed(SPObject *old_ref, SPObject *ref, SPPattern *pat) +void SPPattern::_onRefChanged(SPObject *old_ref, SPObject *ref) { - if (old_ref) { - pat->modified_connection.disconnect(); - } + if (old_ref) { + _modified_connection.disconnect(); + } - if (SP_IS_PATTERN (ref)) { - pat->modified_connection = ref->connectModified(sigc::bind<2>(sigc::ptr_fun(&pattern_ref_modified), pat)); - } + if (SP_IS_PATTERN(ref)) { + _modified_connection = ref->connectModified(sigc::mem_fun(this, &SPPattern::_onRefModified)); + } - pattern_ref_modified (ref, 0, pat); + _onRefModified(ref, 0); } -/** -Gets called when the referenced <pattern> is changed -*/ -static void pattern_ref_modified (SPObject */*ref*/, guint /*flags*/, SPPattern *pattern) +void SPPattern::_onRefModified(SPObject * /*ref*/, guint /*flags*/) { - if ( SP_IS_OBJECT(pattern) ) { - pattern->requestModified(SP_OBJECT_MODIFIED_FLAG); - } + requestModified(SP_OBJECT_MODIFIED_FLAG); // Conditional to avoid causing infinite loop if there's a cycle in the href chain. } - -/** -Count how many times pat is used by the styles of o and its descendants -*/ -static guint -count_pattern_hrefs(SPObject *o, SPPattern *pat) +guint SPPattern::_countHrefs(SPObject *o) const { if (!o) return 1; @@ -322,84 +310,79 @@ count_pattern_hrefs(SPObject *o, SPPattern *pat) guint i = 0; SPStyle *style = o->style; - if (style - && style->fill.isPaintserver() - && SP_IS_PATTERN(SP_STYLE_FILL_SERVER(style)) - && SP_PATTERN(SP_STYLE_FILL_SERVER(style)) == pat) - { - i ++; + if (style && style->fill.isPaintserver() && SP_IS_PATTERN(SP_STYLE_FILL_SERVER(style)) && + SP_PATTERN(SP_STYLE_FILL_SERVER(style)) == this) { + i++; } - if (style - && style->stroke.isPaintserver() - && SP_IS_PATTERN(SP_STYLE_STROKE_SERVER(style)) - && SP_PATTERN(SP_STYLE_STROKE_SERVER(style)) == pat) - { - i ++; + if (style && style->stroke.isPaintserver() && SP_IS_PATTERN(SP_STYLE_STROKE_SERVER(style)) && + SP_PATTERN(SP_STYLE_STROKE_SERVER(style)) == this) { + i++; } - for ( SPObject *child = o->firstChild(); child != NULL; child = child->next ) { - i += count_pattern_hrefs(child, pat); + for (SPObject *child = o->firstChild(); child != NULL; child = child->next) { + i += _countHrefs(child); } return i; } -SPPattern *pattern_chain(SPPattern *pattern) +SPPattern *SPPattern::_chain() const { - SPDocument *document = pattern->document; - Inkscape::XML::Document *xml_doc = document->getReprDoc(); + Inkscape::XML::Document *xml_doc = document->getReprDoc(); Inkscape::XML::Node *defsrepr = document->getDefs()->getRepr(); Inkscape::XML::Node *repr = xml_doc->createElement("svg:pattern"); repr->setAttribute("inkscape:collect", "always"); - gchar *parent_ref = g_strconcat("#", pattern->getRepr()->attribute("id"), NULL); - repr->setAttribute("xlink:href", parent_ref); - g_free (parent_ref); + Glib::ustring parent_ref = Glib::ustring::compose("#%1", getRepr()->attribute("id")); + repr->setAttribute("xlink:href", parent_ref); defsrepr->addChild(repr, NULL); const gchar *child_id = repr->attribute("id"); SPObject *child = document->getObjectById(child_id); - g_assert (SP_IS_PATTERN (child)); + g_assert(SP_IS_PATTERN(child)); - return SP_PATTERN (child); + return SP_PATTERN(child); } -SPPattern * -sp_pattern_clone_if_necessary (SPItem *item, SPPattern *pattern, const gchar *property) +SPPattern *SPPattern::clone_if_necessary(SPItem *item, const gchar *property) { - if (!pattern->href || pattern->hrefcount > count_pattern_hrefs(item, pattern)) { - pattern = pattern_chain (pattern); - gchar *href = g_strconcat("url(#", pattern->getRepr()->attribute("id"), ")", NULL); + SPPattern *pattern = this; + if (pattern->href.empty() || pattern->hrefcount > _countHrefs(item)) { + pattern = _chain(); + Glib::ustring href = Glib::ustring::compose("url(#%1)", pattern->getRepr()->attribute("id")); - SPCSSAttr *css = sp_repr_css_attr_new (); - sp_repr_css_set_property (css, property, href); + SPCSSAttr *css = sp_repr_css_attr_new(); + sp_repr_css_set_property(css, property, href.c_str()); sp_repr_css_change_recursive(item->getRepr(), css, "style"); } return pattern; } -void -sp_pattern_transform_multiply (SPPattern *pattern, Geom::Affine postmul, bool set) +void SPPattern::transform_multiply(Geom::Affine postmul, bool set) { // this formula is for a different interpretation of pattern transforms as described in (*) in sp-pattern.cpp // for it to work, we also need sp_object_read_attr( item, "transform"); - //pattern->patternTransform = premul * item->transform * pattern->patternTransform * item->transform.inverse() * postmul; + // pattern->patternTransform = premul * item->transform * pattern->patternTransform * item->transform.inverse() * + // postmul; // otherwise the formula is much simpler if (set) { - pattern->patternTransform = postmul; - } else { - pattern->patternTransform = pattern_patternTransform(pattern) * postmul; + _pattern_transform = postmul; } - pattern->patternTransform_set = TRUE; + else { + _pattern_transform = getTransform() * postmul; + } + _pattern_transform_set = true; - gchar *c=sp_svg_transform_write(pattern->patternTransform); - pattern->getRepr()->setAttribute("patternTransform", c); - g_free(c); + Glib::ustring c = sp_svg_transform_write(_pattern_transform); + getRepr()->setAttribute("patternTransform", c); } -const gchar *pattern_tile(GSList *reprs, Geom::Rect bounds, SPDocument *document, Geom::Affine transform, Geom::Affine move) +const gchar *SPPattern::produce(const std::vector<Inkscape::XML::Node *> &reprs, Geom::Rect bounds, + SPDocument *document, Geom::Affine transform, Geom::Affine move) { + typedef std::vector<Inkscape::XML::Node *>::const_iterator NodePtrIterator; + Inkscape::XML::Document *xml_doc = document->getReprDoc(); Inkscape::XML::Node *defsrepr = document->getDefs()->getRepr(); @@ -408,20 +391,19 @@ const gchar *pattern_tile(GSList *reprs, Geom::Rect bounds, SPDocument *document sp_repr_set_svg_double(repr, "width", bounds.dimensions()[Geom::X]); sp_repr_set_svg_double(repr, "height", bounds.dimensions()[Geom::Y]); - gchar *t=sp_svg_transform_write(transform); + Glib::ustring t = sp_svg_transform_write(transform); repr->setAttribute("patternTransform", t); - g_free(t); defsrepr->appendChild(repr); const gchar *pat_id = repr->attribute("id"); SPObject *pat_object = document->getObjectById(pat_id); - for (GSList *i = reprs; i != NULL; i = i->next) { - Inkscape::XML::Node *node = (Inkscape::XML::Node *)(i->data); + for (NodePtrIterator i = reprs.begin(); i != reprs.end(); i++) { + Inkscape::XML::Node *node = *i; SPItem *copy = SP_ITEM(pat_object->appendChildRepr(node)); Geom::Affine dup_transform; - if (!sp_svg_transform_read (node->attribute("transform"), &dup_transform)) + if (!sp_svg_transform_read(node->attribute("transform"), &dup_transform)) dup_transform = Geom::identity(); dup_transform *= move; @@ -432,14 +414,15 @@ const gchar *pattern_tile(GSList *reprs, Geom::Rect bounds, SPDocument *document return pat_id; } -SPPattern *pattern_getroot(SPPattern *pat) +SPPattern *SPPattern::rootPattern() { - for (SPPattern *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) { - if ( pat_i->firstChild() ) { // find the first one with children + for (SPPattern *pat_i = this; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) { + if (pat_i->firstChild()) { // find the first one with children return pat_i; } } - return pat; // document is broken, we can't get to root; but at least we can return pat which is supposedly a valid pattern + return this; // document is broken, we can't get to root; but at least we can return pat which is supposedly a valid + // pattern } @@ -447,73 +430,73 @@ SPPattern *pattern_getroot(SPPattern *pat) // Access functions that look up fields up the chain of referenced patterns and return the first one which is set // FIXME: all of them must use chase_hrefs the same as in SPGradient, to avoid lockup on circular refs -guint pattern_patternUnits (SPPattern const *pat) +SPPattern::PatternUnits SPPattern::patternUnits() const { - for (SPPattern const *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) { - if (pat_i->patternUnits_set) - return pat_i->patternUnits; + for (SPPattern const *pat_i = this; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) { + if (pat_i->_pattern_units_set) + return pat_i->_pattern_units; } - return pat->patternUnits; + return _pattern_units; } -guint pattern_patternContentUnits (SPPattern const *pat) +SPPattern::PatternUnits SPPattern::patternContentUnits() const { - for (SPPattern const *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) { - if (pat_i->patternContentUnits_set) - return pat_i->patternContentUnits; + for (SPPattern const *pat_i = this; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) { + if (pat_i->_pattern_content_units_set) + return pat_i->_pattern_content_units; } - return pat->patternContentUnits; + return _pattern_content_units; } -Geom::Affine const &pattern_patternTransform(SPPattern const *pat) +Geom::Affine const &SPPattern::getTransform() const { - for (SPPattern const *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) { - if (pat_i->patternTransform_set) - return pat_i->patternTransform; + for (SPPattern const *pat_i = this; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) { + if (pat_i->_pattern_transform_set) + return pat_i->_pattern_transform; } - return pat->patternTransform; + return _pattern_transform; } -gdouble pattern_x (SPPattern const *pat) +gdouble SPPattern::x() const { - for (SPPattern const *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) { - if (pat_i->x._set) - return pat_i->x.computed; + for (SPPattern 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 pattern_y (SPPattern const *pat) +gdouble SPPattern::y() const { - for (SPPattern const *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) { - if (pat_i->y._set) - return pat_i->y.computed; + for (SPPattern 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 pattern_width (SPPattern const* pat) +gdouble SPPattern::width() const { - for (SPPattern const *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) { - if (pat_i->width._set) - return pat_i->width.computed; + for (SPPattern const *pat_i = this; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) { + if (pat_i->_width._set) + return pat_i->_width.computed; } return 0; } -gdouble pattern_height (SPPattern const *pat) +gdouble SPPattern::height() const { - for (SPPattern const *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) { - if (pat_i->height._set) - return pat_i->height.computed; + for (SPPattern const *pat_i = this; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) { + if (pat_i->_height._set) + return pat_i->_height.computed; } return 0; } -Geom::OptRect pattern_viewBox (SPPattern const *pat) +Geom::OptRect SPPattern::viewbox() const { Geom::OptRect viewbox; - for (SPPattern const *pat_i = pat; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) { + for (SPPattern const *pat_i = this; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) { if (pat_i->viewBox_set) { viewbox = pat_i->viewBox; break; @@ -522,10 +505,10 @@ Geom::OptRect pattern_viewBox (SPPattern const *pat) return viewbox; } -static bool pattern_hasItemChildren (SPPattern const *pat) +bool SPPattern::_hasItemChildren() const { bool hasChildren = false; - for (SPObject const *child = pat->firstChild() ; child && !hasChildren ; child = child->getNext() ) { + for (SPObject const *child = firstChild(); child && !hasChildren; child = child->getNext()) { if (SP_IS_ITEM(child)) { hasChildren = true; } @@ -535,15 +518,16 @@ static bool pattern_hasItemChildren (SPPattern const *pat) bool SPPattern::isValid() const { - double tile_width = pattern_width(this); - double tile_height = pattern_height(this); + double tile_width = width(); + double tile_height = height(); - if (tile_width <= 0 || tile_height <= 0) - return false; - return true; + if (tile_width <= 0 || tile_height <= 0) + return false; + return true; } -cairo_pattern_t* SPPattern::pattern_new(cairo_t *base_ct, Geom::OptRect const &bbox, double opacity) { +cairo_pattern_t *SPPattern::pattern_new(cairo_t *base_ct, Geom::OptRect const &bbox, double opacity) +{ bool needs_opacity = (1.0 - opacity) >= 1e-3; bool visible = opacity >= 1e-3; @@ -557,28 +541,28 @@ cairo_pattern_t* SPPattern::pattern_new(cairo_t *base_ct, Geom::OptRect const &b for (SPPattern *pat_i = this; pat_i != NULL; pat_i = pat_i->ref ? pat_i->ref->getObject() : NULL) { // find the first one with item children - if (pat_i && SP_IS_OBJECT(pat_i) && pattern_hasItemChildren(pat_i)) { + if (pat_i && SP_IS_OBJECT(pat_i) && pat_i->_hasItemChildren()) { shown = pat_i; break; // do not go further up the chain if children are found } } if (!shown) { - return cairo_pattern_create_rgba(0,0,0,0); + return cairo_pattern_create_rgba(0, 0, 0, 0); } /* Create drawing for rendering */ Inkscape::Drawing drawing; - unsigned int dkey = SPItem::display_key_new (1); + unsigned int dkey = SPItem::display_key_new(1); Inkscape::DrawingGroup *root = new Inkscape::DrawingGroup(drawing); drawing.setRoot(root); - for (SPObject *child = shown->firstChild(); child != NULL; child = child->getNext() ) { - if (SP_IS_ITEM (child)) { + for (SPObject *child = shown->firstChild(); child != NULL; child = child->getNext()) { + if (SP_IS_ITEM(child)) { // for each item in pattern, show it on our drawing, add to the group, // and connect to the release signal in case the item gets deleted Inkscape::DrawingItem *cai; - cai = SP_ITEM(child)->invoke_show (drawing, dkey, SP_ITEM_SHOW_DISPLAY); + cai = SP_ITEM(child)->invoke_show(drawing, dkey, SP_ITEM_SHOW_DISPLAY); root->appendChild(cai); } } @@ -591,40 +575,41 @@ cairo_pattern_t* SPPattern::pattern_new(cairo_t *base_ct, Geom::OptRect const &b // * "x", "y", and "patternTransform" transform tile to user space after tile is generated. // These functions recursively search up the tree to find the values. - double tile_x = pattern_x(this); - double tile_y = pattern_y(this); - double tile_width = pattern_width(this); - double tile_height = pattern_height(this); - if ( bbox && (pattern_patternUnits(this) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) ) { - tile_x *= bbox->width(); - tile_y *= bbox->height(); - tile_width *= bbox->width(); + double tile_x = x(); + double tile_y = y(); + double tile_width = width(); + double tile_height = height(); + if (bbox && (patternUnits() == UNITS_OBJECTBOUNDINGBOX)) { + tile_x *= bbox->width(); + tile_y *= bbox->height(); + tile_width *= bbox->width(); tile_height *= bbox->height(); } // Pattern size in pattern space Geom::Rect pattern_tile = Geom::Rect::from_xywh(0, 0, tile_width, tile_height); - + // Content to tile (pattern space) Geom::Affine content2ps; - Geom::OptRect effective_view_box = pattern_viewBox(this); + Geom::OptRect effective_view_box = viewbox(); if (effective_view_box) { - // viewBox to pattern server (using SPViewBox) + // viewBox to pattern server (using SPViewBox) viewBox = *effective_view_box; c2p.setIdentity(); - apply_viewbox( pattern_tile ); + apply_viewbox(pattern_tile); content2ps = c2p; - } else { + } + else { // Content to bbox - if (bbox && (pattern_patternContentUnits(this) == SP_PATTERN_UNITS_OBJECTBOUNDINGBOX) ) { - content2ps = Geom::Affine(bbox->width(), 0.0, 0.0, bbox->height(), 0,0); + if (bbox && (patternContentUnits() == UNITS_OBJECTBOUNDINGBOX)) { + content2ps = Geom::Affine(bbox->width(), 0.0, 0.0, bbox->height(), 0, 0); } } // Tile (pattern space) to user. - Geom::Affine ps2user = Geom::Translate(tile_x,tile_y) * pattern_patternTransform(this); + Geom::Affine ps2user = Geom::Translate(tile_x, tile_y) * getTransform(); // Transform of object with pattern (includes screen scaling) @@ -644,7 +629,7 @@ cairo_pattern_t* SPPattern::pattern_new(cairo_t *base_ct, Geom::OptRect const &b // to find the optimum tile size for rendering // c is number of pixels in buffer x and y. // Scale factor of 1.1 is too small... see bug #1251039 - Geom::Point c(pattern_tile.dimensions()*ps2user.descrim()*full.descrim()*2.0); + Geom::Point c(pattern_tile.dimensions() * ps2user.descrim() * full.descrim() * 2.0); // Create drawing surface with size of pattern tile (in pattern space) but with number of pixels // based on required resolution (c). @@ -660,16 +645,16 @@ cairo_pattern_t* SPPattern::pattern_new(cairo_t *base_ct, Geom::OptRect const &b } // TODO: make sure there are no leaks. - Inkscape::UpdateContext ctx; // UpdateContext is structure with only ctm! + Inkscape::UpdateContext ctx; // UpdateContext is structure with only ctm! ctx.ctm = content2ps * pattern_surface.drawingTransform(); - dc.transform( pattern_surface.drawingTransform().inverse() ); + dc.transform(pattern_surface.drawingTransform().inverse()); drawing.update(Geom::IntRect::infinite(), ctx); // Render drawing to pattern_surface via drawing context, this calls root->render // which is really DrawingItem->render(). drawing.render(dc, one_tile); - for (SPObject *child = shown->firstChild() ; child != NULL; child = child->getNext() ) { - if (SP_IS_ITEM (child)) { + for (SPObject *child = shown->firstChild(); child != NULL; child = child->getNext()) { + if (SP_IS_ITEM(child)) { SP_ITEM(child)->invoke_hide(dkey); } } @@ -685,12 +670,12 @@ cairo_pattern_t* SPPattern::pattern_new(cairo_t *base_ct, Geom::OptRect const &b if (needs_opacity) { dc.popGroupToSource(); // pop raw pattern - dc.paint(opacity); // apply opacity + 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. - ink_cairo_pattern_set_matrix(cp, ps2user.inverse() * pattern_surface.drawingTransform() ); + ink_cairo_pattern_set_matrix(cp, ps2user.inverse() * pattern_surface.drawingTransform()); cairo_pattern_set_extend(cp, CAIRO_EXTEND_REPEAT); diff --git a/src/sp-pattern.h b/src/sp-pattern.h index f021101e2..a5e7be1d4 100644 --- a/src/sp-pattern.h +++ b/src/sp-pattern.h @@ -1,9 +1,6 @@ -#ifndef SEEN_SP_PATTERN_H -#define SEEN_SP_PATTERN_H - -/* +/** @file * SVG <pattern> implementation - * + *//* * Author: * Lauris Kaplinski <lauris@kaplinski.com> * Abhishek Sharma @@ -13,95 +10,130 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ -#define SP_PATTERN(obj) (dynamic_cast<SPPattern*>((SPObject*)obj)) -#define SP_IS_PATTERN(obj) (dynamic_cast<const SPPattern*>((SPObject*)obj) != NULL) +#ifndef SEEN_SP_PATTERN_H +#define SEEN_SP_PATTERN_H -class SPPatternReference; -class SPItem; -typedef struct _GSList GSList; +#include <list> +#include <stddef.h> +#include <glibmm/ustring.h> +#include <sigc++/connection.h> #include "svg/svg-length.h" #include "sp-paint-server.h" #include "uri-references.h" #include "viewbox.h" -#include <sigc++/connection.h> +class SPPatternReference; +class SPItem; +namespace Inkscape { +namespace XML { + +class Node; +} +} + +#define SP_PATTERN(obj) (dynamic_cast<SPPattern *>((SPObject *)obj)) +#define SP_IS_PATTERN(obj) (dynamic_cast<const SPPattern *>((SPObject *)obj) != NULL) class SPPattern : public SPPaintServer, public SPViewBox { public: - SPPattern(); - virtual ~SPPattern(); + enum PatternUnits { UNITS_USERSPACEONUSE, UNITS_OBJECTBOUNDINGBOX }; + + SPPattern(); + virtual ~SPPattern(); /* Reference (href) */ - char *href; + Glib::ustring href; SPPatternReference *ref; - /* patternUnits and patternContentUnits attribute */ - unsigned int patternUnits : 1; - unsigned int patternUnits_set : 1; - unsigned int patternContentUnits : 1; - unsigned int patternContentUnits_set : 1; - /* patternTransform attribute */ - Geom::Affine patternTransform; - unsigned int patternTransform_set : 1; - /* Tile rectangle */ - SVGLength x; - SVGLength y; - SVGLength width; - SVGLength height; - - sigc::connection modified_connection; + gdouble x() const; + gdouble y() const; + gdouble width() const; + gdouble height() const; + Geom::OptRect viewbox() const; + SPPattern::PatternUnits patternUnits() const; + SPPattern::PatternUnits patternContentUnits() const; + Geom::Affine const &getTransform() const; + SPPattern *rootPattern(); // TODO: const + + SPPattern *clone_if_necessary(SPItem *item, const gchar *property); + void transform_multiply(Geom::Affine postmul, bool set); + + /** + * @brief create a new pattern in XML tree + * @return created pattern id + */ + static const gchar *produce(const std::vector<Inkscape::XML::Node *> &reprs, Geom::Rect bounds, + SPDocument *document, Geom::Affine transform, Geom::Affine move); bool isValid() const; - virtual cairo_pattern_t* pattern_new(cairo_t *ct, Geom::OptRect const &bbox, double opacity); + virtual cairo_pattern_t *pattern_new(cairo_t *ct, Geom::OptRect const &bbox, double opacity); 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); - virtual void modified(unsigned int flags); + 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); + virtual void modified(unsigned int flags); + +private: + bool _hasItemChildren() const; + void _getChildren(std::list<SPObject *> &l); + SPPattern *_chain() const; + + /** + Count how many times pattern is used by the styles of o and its descendants + */ + guint _countHrefs(SPObject *o) const; + + /** + Gets called when the pattern is reattached to another <pattern> + */ + void _onRefChanged(SPObject *old_ref, SPObject *ref); + + /** + Gets called when the referenced <pattern> is changed + */ + void _onRefModified(SPObject *ref, guint flags); + + /* patternUnits and patternContentUnits attribute */ + PatternUnits _pattern_units : 1; + bool _pattern_units_set : 1; + PatternUnits _pattern_content_units : 1; + bool _pattern_content_units_set : 1; + /* patternTransform attribute */ + Geom::Affine _pattern_transform; + bool _pattern_transform_set : 1; + /* Tile rectangle */ + SVGLength _x; + SVGLength _y; + SVGLength _width; + SVGLength _height; + + sigc::connection _modified_connection; }; class SPPatternReference : public Inkscape::URIReference { public: - SPPatternReference (SPObject *obj) : URIReference(obj) {} - SPPattern *getObject() const { + SPPatternReference(SPObject *obj) + : URIReference(obj) + { + } + + SPPattern *getObject() const + { return reinterpret_cast<SPPattern *>(URIReference::getObject()); } protected: virtual bool _acceptObject(SPObject *obj) const { - return SP_IS_PATTERN (obj); + return SP_IS_PATTERN (obj)&& URIReference::_acceptObject(obj); } }; -enum { - SP_PATTERN_UNITS_USERSPACEONUSE, - SP_PATTERN_UNITS_OBJECTBOUNDINGBOX -}; - -unsigned int pattern_users (SPPattern *pattern); -SPPattern *pattern_chain (SPPattern *pattern); -SPPattern *sp_pattern_clone_if_necessary (SPItem *item, SPPattern *pattern, const char *property); -void sp_pattern_transform_multiply (SPPattern *pattern, Geom::Affine postmul, bool set); - -const char *pattern_tile (GSList *reprs, Geom::Rect bounds, SPDocument *document, Geom::Affine transform, Geom::Affine move); - -SPPattern *pattern_getroot (SPPattern *pat); - -unsigned int pattern_patternUnits (SPPattern const *pat); -unsigned int pattern_patternContentUnits (SPPattern const *pat); -Geom::Affine const &pattern_patternTransform(SPPattern const *pat); -double pattern_x (SPPattern const *pat); -double pattern_y (SPPattern const *pat); -double pattern_width (SPPattern const *pat); -double pattern_height (SPPattern const *pat); -Geom::OptRect pattern_viewBox (SPPattern const *pat); - #endif // SEEN_SP_PATTERN_H /* diff --git a/src/sp-polygon.cpp b/src/sp-polygon.cpp index af71280d5..ced485f12 100644 --- a/src/sp-polygon.cpp +++ b/src/sp-polygon.cpp @@ -16,8 +16,7 @@ #include "display/curve.h" #include <glibmm/i18n.h> #include <2geom/pathvector.h> -#include <2geom/bezier-curve.h> -#include <2geom/hvlinesegment.h> +#include <2geom/curves.h> #include "helper/geom-curves.h" #include "svg/stringstream.h" #include "xml/repr.h" diff --git a/src/sp-switch.cpp b/src/sp-switch.cpp index 66cec3637..cfc0e7d8b 100644 --- a/src/sp-switch.cpp +++ b/src/sp-switch.cpp @@ -41,21 +41,22 @@ SPObject *SPSwitch::_evaluateFirst() { return first; } -GSList *SPSwitch::_childList(bool add_ref, SPObject::Action action) { +std::vector<SPObject*> SPSwitch::_childList(bool add_ref, SPObject::Action action) { if ( action != SPObject::ActionGeneral ) { return this->childList(add_ref, action); } SPObject *child = _evaluateFirst(); + std::vector<SPObject*> x; if (NULL == child) - return NULL; + return x; if (add_ref) { //g_object_ref (G_OBJECT (child)); sp_object_ref(child); } - - return g_slist_prepend (NULL, child); + x.push_back(child); + return x; } const char *SPSwitch::displayName() const { @@ -95,10 +96,9 @@ void SPSwitch::_reevaluate(bool /*add_to_drawing*/) { _releaseLastItem(_cached_item); - for ( GSList *l = _childList(false, SPObject::ActionShow); - NULL != l ; l = g_slist_remove (l, l->data)) - { - SPObject *o = SP_OBJECT (l->data); + std::vector<SPObject*> item_list = _childList(false, SPObject::ActionShow); + for ( std::vector<SPObject*>::const_reverse_iterator iter=item_list.rbegin();iter!=item_list.rend();iter++) { + SPObject *o = *iter; if ( !SP_IS_ITEM (o) ) { continue; } @@ -130,10 +130,10 @@ void SPSwitch::_releaseLastItem(SPObject *obj) void SPSwitch::_showChildren (Inkscape::Drawing &drawing, Inkscape::DrawingItem *ai, unsigned int key, unsigned int flags) { SPObject *evaluated_child = this->_evaluateFirst(); - GSList *l = this->_childList(false, SPObject::ActionShow); + std::vector<SPObject*> l = this->_childList(false, SPObject::ActionShow); - while (l) { - SPObject *o = SP_OBJECT (l->data); + for ( std::vector<SPObject*>::const_reverse_iterator iter=l.rbegin();iter!=l.rend();iter++) { + SPObject *o = *iter; if (SP_IS_ITEM (o)) { SPItem * child = SP_ITEM(o); @@ -144,8 +144,6 @@ void SPSwitch::_showChildren (Inkscape::Drawing &drawing, Inkscape::DrawingItem ai->appendChild(ac); } } - - l = g_slist_remove (l, o); } } diff --git a/src/sp-switch.h b/src/sp-switch.h index 4fce1f5a6..7152e1b82 100644 --- a/src/sp-switch.h +++ b/src/sp-switch.h @@ -29,7 +29,7 @@ public: void resetChildEvaluated() { _reevaluate(); } - GSList *_childList(bool add_ref, SPObject::Action action); + std::vector<SPObject*> _childList(bool add_ref, SPObject::Action action); virtual void _showChildren (Inkscape::Drawing &drawing, Inkscape::DrawingItem *ai, unsigned int key, unsigned int flags); SPObject *_evaluateFirst(); diff --git a/src/sp-tag-use-reference.cpp b/src/sp-tag-use-reference.cpp index 220cd16d1..9fcb31fd1 100644 --- a/src/sp-tag-use-reference.cpp +++ b/src/sp-tag-use-reference.cpp @@ -24,14 +24,7 @@ bool SPTagUseReference::_acceptObject(SPObject * const obj) const { if (SP_IS_ITEM(obj)) { - SPObject * const owner = getOwner(); - // Refuse references to us or to an ancestor. - for ( SPObject *iter = owner ; iter ; iter = iter->parent ) { - if ( iter == obj ) { - return false; - } - } - return true; + return URIReference::_acceptObject(obj); } else { return false; } diff --git a/src/sp-text.cpp b/src/sp-text.cpp index 0dca42bb4..d351e58e3 100644 --- a/src/sp-text.cpp +++ b/src/sp-text.cpp @@ -149,7 +149,7 @@ void SPText::remove_child(Inkscape::XML::Node *rch) { void SPText::update(SPCtx *ctx, guint flags) { unsigned childflags = (flags & SP_OBJECT_MODIFIED_CASCADE); if (flags & SP_OBJECT_MODIFIED_FLAG) { - childflags |= SP_OBJECT_PARENT_MODIFIED_FLAG; + childflags |= SP_OBJECT_PARENT_MODIFIED_FLAG; } // Create temporary list of children @@ -519,7 +519,7 @@ unsigned SPText::_buildLayoutInput(SPObject *root, Inkscape::Text::Layout::Optio } } } - + if (SP_IS_TEXT(root)) { SP_TEXT(root)->attributes.mergeInto(&optional_attrs, parent_optional_attrs, parent_attrs_offset, true, true); if (SP_TEXT(root)->attributes.getTextLength()->_set) { // set textLength on the entire layout, see note in TNG-Layout.h @@ -571,7 +571,7 @@ unsigned SPText::_buildLayoutInput(SPObject *root, Inkscape::Text::Layout::Optio child_attrs_offset--; } } - + for (SPObject *child = root->firstChild() ; child ; child = child->getNext() ) { SPString *str = dynamic_cast<SPString *>(child); if (str) { @@ -691,15 +691,15 @@ bool TextTagAttributes::readSingleAttribute(unsigned key, gchar const *value) case SP_ATTR_DX: attr_vector = &attributes.dx; break; case SP_ATTR_DY: attr_vector = &attributes.dy; break; case SP_ATTR_ROTATE: attr_vector = &attributes.rotate; break; - case SP_ATTR_TEXTLENGTH: - attributes.textLength.readOrUnset(value); - return true; + case SP_ATTR_TEXTLENGTH: + attributes.textLength.readOrUnset(value); + return true; break; - case SP_ATTR_LENGTHADJUST: - attributes.lengthAdjust = (value && !strcmp(value, "spacingAndGlyphs")? - Inkscape::Text::Layout::LENGTHADJUST_SPACINGANDGLYPHS : + case SP_ATTR_LENGTHADJUST: + attributes.lengthAdjust = (value && !strcmp(value, "spacingAndGlyphs")? + Inkscape::Text::Layout::LENGTHADJUST_SPACINGANDGLYPHS : Inkscape::Text::Layout::LENGTHADJUST_SPACING); // default is "spacing" - return true; + return true; break; default: return false; } @@ -724,7 +724,7 @@ void TextTagAttributes::writeTo(Inkscape::XML::Node *node) const node->setAttribute("lengthAdjust", "spacing"); } else if (attributes.lengthAdjust == Inkscape::Text::Layout::LENGTHADJUST_SPACINGANDGLYPHS) { node->setAttribute("lengthAdjust", "spacingAndGlyphs"); - } + } } } @@ -734,7 +734,7 @@ void TextTagAttributes::writeSingleAttributeLength(Inkscape::XML::Node *node, gc gchar single_value_string[32]; g_ascii_formatd(single_value_string, sizeof (single_value_string), "%.8g", length.computed); node->setAttribute(key, single_value_string); - } else + } else node->setAttribute(key, NULL); } @@ -802,7 +802,7 @@ void TextTagAttributes::mergeInto(Inkscape::Text::Layout::OptionalTextTagAttrs * output->textLength.unit = attributes.textLength.unit; output->textLength._set = attributes.textLength._set; output->lengthAdjust = attributes.lengthAdjust; - } + } } void TextTagAttributes::mergeSingleAttribute(std::vector<SVGLength> *output_list, std::vector<SVGLength> const &parent_list, unsigned parent_offset, std::vector<SVGLength> const *overlay_list) diff --git a/src/sp-tref-reference.cpp b/src/sp-tref-reference.cpp index e82f575e0..7c6ff00e7 100644 --- a/src/sp-tref-reference.cpp +++ b/src/sp-tref-reference.cpp @@ -21,7 +21,7 @@ bool SPTRefReference::_acceptObject(SPObject * const obj) const { SPObject *owner = getOwner(); if (SP_IS_TREF(owner)) - return sp_tref_reference_allowed(SP_TREF(getOwner()), obj); + return URIReference::_acceptObject(obj); else return false; } diff --git a/src/sp-use-reference.cpp b/src/sp-use-reference.cpp index 642cfede8..f0b2985d2 100644 --- a/src/sp-use-reference.cpp +++ b/src/sp-use-reference.cpp @@ -25,18 +25,7 @@ bool SPUseReference::_acceptObject(SPObject * const obj) const { - if (SP_IS_ITEM(obj)) { - SPObject * const owner = getOwner(); - // Refuse references to us or to an ancestor. - for ( SPObject *iter = owner ; iter ; iter = iter->parent ) { - if ( iter == obj ) { - return false; - } - } - return true; - } else { - return false; - } + return URIReference::_acceptObject(obj); } diff --git a/src/sp-use.cpp b/src/sp-use.cpp index 239f487a4..c8a0830c1 100644 --- a/src/sp-use.cpp +++ b/src/sp-use.cpp @@ -22,6 +22,8 @@ #include "display/drawing-group.h" #include "attributes.h" #include "document.h" +#include "sp-clippath.h" +#include "sp-mask.h" #include "sp-factory.h" #include "sp-flowregion.h" #include "uri.h" @@ -426,16 +428,43 @@ void SPUse::move_compensate(Geom::Affine const *mp) { return; Geom::Affine m(*mp); + Geom::Affine t = this->get_parent_transform(); + Geom::Affine clone_move = t.inverse() * m * t; // this is not a simple move, do not try to compensate - if (!(m.isTranslation())) + if (!(m.isTranslation())){ + //BUT move clippaths accordingly. + //if clone has a clippath, move it accordingly + if(clip_ref->getObject()){ + SPObject *clip = clip_ref->getObject()->firstChild() ; + while(clip){ + SPItem *item = (SPItem*) clip; + if(item){ + item->transform *= m; + Geom::Affine identity; + item->doWriteTransform(clip->getRepr(),item->transform, &identity); + } + clip = clip->getNext(); + } + } + if(mask_ref->getObject()){ + SPObject *mask = mask_ref->getObject()->firstChild() ; + while(mask){ + SPItem *item = (SPItem*) mask; + if(item){ + item->transform *= m; + Geom::Affine identity; + item->doWriteTransform(mask->getRepr(),item->transform, &identity); + } + mask = mask->getNext(); + } + } return; + } // restore item->transform field from the repr, in case it was changed by seltrans this->readAttr ("transform"); - Geom::Affine t = this->get_parent_transform(); - Geom::Affine clone_move = t.inverse() * m * t; // calculate the compensation matrix and the advertized movement matrix Geom::Affine advertized_move; @@ -449,6 +478,33 @@ void SPUse::move_compensate(Geom::Affine const *mp) { g_assert_not_reached(); } + //if clone has a clippath, move it accordingly + if(clip_ref->getObject()){ + SPObject *clip = clip_ref->getObject()->firstChild() ; + while(clip){ + SPItem *item = (SPItem*) clip; + if(item){ + item->transform *= clone_move.inverse(); + Geom::Affine identity; + item->doWriteTransform(clip->getRepr(),item->transform, &identity); + } + clip = clip->getNext(); + } + } + if(mask_ref->getObject()){ + SPObject *mask = mask_ref->getObject()->firstChild() ; + while(mask){ + SPItem *item = (SPItem*) mask; + if(item){ + item->transform *= clone_move.inverse(); + Geom::Affine identity; + item->doWriteTransform(mask->getRepr(),item->transform, &identity); + } + mask = mask->getNext(); + } + } + + // commit the compensation this->transform *= clone_move; this->doWriteTransform(this->getRepr(), this->transform, &advertized_move); diff --git a/src/splivarot.cpp b/src/splivarot.cpp index 46279cbce..726df6e20 100644 --- a/src/splivarot.cpp +++ b/src/splivarot.cpp @@ -47,6 +47,7 @@ #include "xml/repr.h" #include "xml/repr-sorting.h" #include <2geom/pathvector.h> +#include <2geom/svg-path-writer.h> #include "helper/geom.h" #include "livarot/Path.h" @@ -305,8 +306,9 @@ sp_pathvector_boolop(Geom::PathVector const &pathva, Geom::PathVector const &pat delete originaux[0]; delete originaux[1]; - std::vector<Geom::Path> outres = Geom::parse_svg_path(res->svg_dump_path()); - + gchar *result_str = res->svg_dump_path(); + Geom::PathVector outres = Geom::parse_svg_path(result_str); + g_free(result_str); delete res; return outres; @@ -315,7 +317,7 @@ sp_pathvector_boolop(Geom::PathVector const &pathva, Geom::PathVector const &pat /* Convert from a livarot path to a 2geom PathVector */ Geom::PathVector pathliv_to_pathvector(Path *pathliv){ - std::vector<Geom::Path> outres = Geom::parse_svg_path(pathliv->svg_dump_path()); + Geom::PathVector outres = Geom::parse_svg_path(pathliv->svg_dump_path()); return outres; } @@ -326,21 +328,21 @@ void sp_selected_path_boolop(Inkscape::Selection *selection, SPDesktop *desktop, bool_op bop, const unsigned int verb, const Glib::ustring description) { SPDocument *doc = selection->layers()->getDocument(); - GSList *il = (GSList *) selection->itemList(); + std::vector<SPItem*> il= selection->itemList(); // allow union on a single object for the purpose of removing self overlapse (svn log, revision 13334) - if ( (g_slist_length(il) < 2) && (bop != bool_op_union)) { + if ( (il.size() < 2) && (bop != bool_op_union)) { boolop_display_error_message(desktop, _("Select <b>at least 2 paths</b> to perform a boolean operation.")); return; } - else if ( g_slist_length(il) < 1 ) { + else if ( il.size() < 1 ) { boolop_display_error_message(desktop, _("Select <b>at least 1 path</b> to perform a boolean union.")); return; } - g_assert(il != NULL); + g_assert(!il.empty()); - if (g_slist_length(il) > 2) { + if (il.size() > 2) { if (bop == bool_op_diff || bop == bool_op_cut || bop == bool_op_slice ) { boolop_display_error_message(desktop, _("Select <b>exactly 2 paths</b> to perform difference, division, or path cut.")); return; @@ -354,8 +356,8 @@ sp_selected_path_boolop(Inkscape::Selection *selection, SPDesktop *desktop, bool if (bop == bool_op_diff || bop == bool_op_cut || bop == bool_op_slice) { // check in the tree to find which element of the selection list is topmost (for 2-operand commands only) - Inkscape::XML::Node *a = SP_OBJECT(il->data)->getRepr(); - Inkscape::XML::Node *b = SP_OBJECT(il->next->data)->getRepr(); + Inkscape::XML::Node *a = il.front()->getRepr(); + Inkscape::XML::Node *b = il.back()->getRepr(); if (a == NULL || b == NULL) { boolop_display_error_message(desktop, _("Unable to determine the <b>z-order</b> of the objects selected for difference, XOR, division, or path cut.")); @@ -394,38 +396,36 @@ sp_selected_path_boolop(Inkscape::Selection *selection, SPDesktop *desktop, bool } } - il = g_slist_copy(il); - g_assert(il != NULL); + g_assert(!il.empty()); // first check if all the input objects have shapes // otherwise bail out - for (GSList *l = il; l != NULL; l = l->next) + for (std::vector<SPItem*>::const_iterator l = il.begin(); l != il.end(); l++) { - SPItem *item = SP_ITEM(l->data); + SPItem *item = *l; if (!SP_IS_SHAPE(item) && !SP_IS_TEXT(item) && !SP_IS_FLOWTEXT(item)) { boolop_display_error_message(desktop, _("One of the objects is <b>not a path</b>, cannot perform boolean operation.")); - g_slist_free(il); return; } } // extract the livarot Paths from the source objects // also get the winding rule specified in the style - int nbOriginaux = g_slist_length(il); + int nbOriginaux = il.size(); std::vector<Path *> originaux(nbOriginaux); std::vector<FillRule> origWind(nbOriginaux); int curOrig; { curOrig = 0; - for (GSList *l = il; l != NULL; l = l->next) + for (std::vector<SPItem*>::const_iterator l = il.begin(); l != il.end(); l++) { // apply live path effects prior to performing boolean operation - if (SP_IS_LPE_ITEM(l->data)) { - SP_LPE_ITEM(l->data)->removeAllPathEffects(true); + if (SP_IS_LPE_ITEM(*l)) { + SP_LPE_ITEM(*l)->removeAllPathEffects(true); } - SPCSSAttr *css = sp_repr_css_attr(reinterpret_cast<SPObject *>(il->data)->getRepr(), "style"); + SPCSSAttr *css = sp_repr_css_attr(reinterpret_cast<SPObject *>(il[0])->getRepr(), "style"); gchar const *val = sp_repr_css_property(css, "fill-rule", NULL); if (val && strcmp(val, "nonzero") == 0) { origWind[curOrig]= fill_nonZero; @@ -435,11 +435,10 @@ sp_selected_path_boolop(Inkscape::Selection *selection, SPDesktop *desktop, bool origWind[curOrig]= fill_nonZero; } - originaux[curOrig] = Path_for_item((SPItem *) l->data, true, true); + originaux[curOrig] = Path_for_item(*l, true, true); if (originaux[curOrig] == NULL || originaux[curOrig]->descr_cmd.size() <= 1) { for (int i = curOrig; i >= 0; i--) delete originaux[i]; - g_slist_free(il); return; } curOrig++; @@ -448,8 +447,9 @@ sp_selected_path_boolop(Inkscape::Selection *selection, SPDesktop *desktop, bool // reverse if needed // note that the selection list keeps its order if ( reverseOrderForOp ) { - Path* swap=originaux[0];originaux[0]=originaux[1];originaux[1]=swap; - FillRule swai=origWind[0]; origWind[0]=origWind[1]; origWind[1]=swai; + using std::swap; + swap(originaux[0], originaux[1]); + swap(origWind[0], origWind[1]); } // and work @@ -472,7 +472,8 @@ sp_selected_path_boolop(Inkscape::Selection *selection, SPDesktop *desktop, bool theShapeA->ConvertToShape(theShape, origWind[0]); curOrig = 1; - for (GSList *l = il->next; l != NULL; l = l->next) { + for (std::vector<SPItem*>::const_iterator l = il.begin(); l != il.end(); l++){ + if(*l==il[0])continue; originaux[curOrig]->ConvertWithBackData(0.1); originaux[curOrig]->Fill(theShape, curOrig); @@ -668,15 +669,13 @@ sp_selected_path_boolop(Inkscape::Selection *selection, SPDesktop *desktop, bool if (res->descr_cmd.size() <= 1) { // only one command, presumably a moveto: it isn't a path - for (GSList *l = il; l != NULL; l = l->next) - { - SP_OBJECT(l->data)->deleteObject(); + for (std::vector<SPItem*>::const_iterator l = il.begin(); l != il.end(); l++){ + (*l)->deleteObject(); } DocumentUndo::done(doc, SP_VERB_NONE, description); selection->clear(); delete res; - g_slist_free(il); return; } @@ -684,19 +683,17 @@ sp_selected_path_boolop(Inkscape::Selection *selection, SPDesktop *desktop, bool SPObject *source; if ( bop == bool_op_diff || bop == bool_op_cut || bop == bool_op_slice ) { if (reverseOrderForOp) { - source = SP_OBJECT(il->data); + source = il[0]; } else { - source = SP_OBJECT(il->next->data); + source = il.back(); } } else { // find out the bottom object - GSList *sorted = g_slist_copy((GSList *) selection->reprList()); + std::vector<Inkscape::XML::Node*> sorted(selection->reprList()); - sorted = g_slist_sort(sorted, (GCompareFunc) sp_repr_compare_position); + sort(sorted.begin(),sorted.end(),sp_repr_compare_position_bool); - source = doc->getObjectByRepr((Inkscape::XML::Node *)sorted->data); - - g_slist_free(sorted); + source = doc->getObjectByRepr(sorted.front()); } // adjust style properties that depend on a possible transform in the source object in order @@ -721,17 +718,16 @@ sp_selected_path_boolop(Inkscape::Selection *selection, SPDesktop *desktop, bool gchar *desc = source->desc(); // remove source paths selection->clear(); - for (GSList *l = il; l != NULL; l = l->next) { + for (std::vector<SPItem*>::const_iterator l = il.begin(); l != il.end(); l++){ // if this is the bottommost object, - if (!strcmp(reinterpret_cast<SPObject *>(l->data)->getRepr()->attribute("id"), id)) { + if (!strcmp(reinterpret_cast<SPObject *>(*l)->getRepr()->attribute("id"), id)) { // delete it so that its clones don't get alerted; this object will be restored shortly, with the same id - SP_OBJECT(l->data)->deleteObject(false); + (*l)->deleteObject(false); } else { // delete the object for real, so that its clones can take appropriate action - SP_OBJECT(l->data)->deleteObject(); + (*l)->deleteObject(); } } - g_slist_free(il); // premultiply by the inverse of parent's repr SPItem *parent_item = SP_ITEM(doc->getObjectByRepr(parent)); @@ -1159,12 +1155,9 @@ sp_selected_path_outline(SPDesktop *desktop) } bool did = false; - - for (GSList *items = g_slist_copy((GSList *) selection->itemList()); - items != NULL; - items = items->next) { - - SPItem *item = SP_ITEM(items->data); + std::vector<SPItem*> il(selection->itemList()); + for (std::vector<SPItem*>::const_iterator l = il.begin(); l != il.end(); l++){ + SPItem *item = *l; if (!SP_IS_SHAPE(item) && !SP_IS_TEXT(item)) continue; @@ -1776,12 +1769,9 @@ sp_selected_path_do_offset(SPDesktop *desktop, bool expand, double prefOffset) } bool did = false; - - for (GSList *items = g_slist_copy((GSList *) selection->itemList()); - items != NULL; - items = items->next) { - - SPItem *item = SP_ITEM(items->data); + std::vector<SPItem*> il(selection->itemList()); + for (std::vector<SPItem*>::const_iterator l = il.begin(); l != il.end(); l++){ + SPItem *item = *l; SPCurve *curve = NULL; if (!SP_IS_SHAPE(item) && !SP_IS_TEXT(item)) @@ -1977,7 +1967,7 @@ sp_selected_path_do_offset(SPDesktop *desktop, bool expand, double prefOffset) static bool sp_selected_path_simplify_items(SPDesktop *desktop, - Inkscape::Selection *selection, GSList *items, + Inkscape::Selection *selection, std::vector<SPItem*> &items, float threshold, bool justCoalesce, float angleLimit, bool breakableAngles, bool modifySelection); @@ -1996,7 +1986,7 @@ sp_selected_path_simplify_item(SPDesktop *desktop, //If this is a group, do the children instead if (SP_IS_GROUP(item)) { - GSList *items = sp_item_group_item_list(SP_GROUP(item)); + std::vector<SPItem*> items = sp_item_group_item_list(SP_GROUP(item)); return sp_selected_path_simplify_items(desktop, selection, items, threshold, justCoalesce, @@ -2121,7 +2111,7 @@ sp_selected_path_simplify_item(SPDesktop *desktop, bool sp_selected_path_simplify_items(SPDesktop *desktop, - Inkscape::Selection *selection, GSList *items, + Inkscape::Selection *selection, std::vector<SPItem*> &items, float threshold, bool justCoalesce, float angleLimit, bool breakableAngles, bool modifySelection) @@ -2147,13 +2137,13 @@ sp_selected_path_simplify_items(SPDesktop *desktop, gdouble simplifySize = selectionSize; int pathsSimplified = 0; - int totalPathCount = g_slist_length(items); + int totalPathCount = items.size(); // set "busy" cursor desktop->setWaitingCursor(); - for (; items != NULL; items = items->next) { - SPItem *item = (SPItem *) items->data; + for (std::vector<SPItem*>::const_iterator l = items.begin(); l != items.end(); l++){ + SPItem *item = *l; if (!(SP_IS_GROUP(item) || SP_IS_SHAPE(item) || SP_IS_TEXT(item))) continue; @@ -2201,7 +2191,7 @@ sp_selected_path_simplify_selection(SPDesktop *desktop, float threshold, bool ju return; } - GSList *items = g_slist_copy((GSList *) selection->itemList()); + std::vector<SPItem*> items(selection->itemList()); bool didSomething = sp_selected_path_simplify_items(desktop, selection, items, threshold, @@ -2273,17 +2263,17 @@ Ancetre(Inkscape::XML::Node *a, Inkscape::XML::Node *who) } // derived from Path_for_item -// there must be some other way to load dest directly from epathv, without going through pathv... Path * Path_for_pathvector(Geom::PathVector const &epathv) { - Geom::PathVector *pathv = new Geom::PathVector; - std::copy(epathv.begin(), epathv.end(), std::back_inserter(*pathv)); - + /*std::cout << "converting to Livarot path" << std::endl; + + Geom::SVGPathWriter wr; + wr.feed(epathv); + std::cout << wr.str() << std::endl;*/ + Path *dest = new Path; - dest->LoadPathVector(*pathv); - delete pathv; - + dest->LoadPathVector(epathv); return dest; } @@ -2294,14 +2284,26 @@ Path_for_item(SPItem *item, bool doTransformation, bool transformFull) if (curve == NULL) return NULL; - + Geom::PathVector *pathv = pathvector_for_curve(item, curve, doTransformation, transformFull, Geom::identity(), Geom::identity()); curve->unref(); - + + /*std::cout << "converting to Livarot path" << std::endl; + + Geom::SVGPathWriter wr; + if (pathv) { + wr.feed(*pathv); + } + std::cout << wr.str() << std::endl;*/ + Path *dest = new Path; dest->LoadPathVector(*pathv); delete pathv; - + + /*gchar *str = dest->svg_dump_path(); + std::cout << "After conversion:\n" << str << std::endl; + g_free(str);*/ + return dest; } @@ -2348,7 +2350,7 @@ pathvector_for_curve(SPItem *item, SPCurve *curve, bool doTransformation, bool t } else { *dest *= extraPreAffine * extraPostAffine; } - + return dest; } diff --git a/src/style-enums.h b/src/style-enums.h index f52752018..dfc99282c 100644 --- a/src/style-enums.h +++ b/src/style-enums.h @@ -72,6 +72,75 @@ enum SPCSSFontStretch { SP_CSS_FONT_STRETCH_WIDER }; +// Can select more than one +enum SPCSSFontVariantLigatures { + SP_CSS_FONT_VARIANT_LIGATURES_NONE = 0, + SP_CSS_FONT_VARIANT_LIGATURES_COMMON = 1, + SP_CSS_FONT_VARIANT_LIGATURES_DISCRETIONARY = 2, + SP_CSS_FONT_VARIANT_LIGATURES_HISTORICAL = 4, + SP_CSS_FONT_VARIANT_LIGATURES_CONTEXTUAL = 8, + SP_CSS_FONT_VARIANT_LIGATURES_NORMAL = 9, // Special case + SP_CSS_FONT_VARIANT_LIGATURES_NOCOMMON = 16, + SP_CSS_FONT_VARIANT_LIGATURES_NODISCRETIONARY = 32, + SP_CSS_FONT_VARIANT_LIGATURES_NOHISTORICAL = 64, + SP_CSS_FONT_VARIANT_LIGATURES_NOCONTEXTUAL = 128 +}; + +enum SPCSSFontVariantPosition { + SP_CSS_FONT_VARIANT_POSITION_NORMAL = 1, + SP_CSS_FONT_VARIANT_POSITION_SUB = 2, + SP_CSS_FONT_VARIANT_POSITION_SUPER = 4 +}; + +enum SPCSSFontVariantCaps { + SP_CSS_FONT_VARIANT_CAPS_NORMAL = 1, + SP_CSS_FONT_VARIANT_CAPS_SMALL = 2, + SP_CSS_FONT_VARIANT_CAPS_ALL_SMALL = 4, + SP_CSS_FONT_VARIANT_CAPS_PETITE = 8, + SP_CSS_FONT_VARIANT_CAPS_ALL_PETITE = 16, + SP_CSS_FONT_VARIANT_CAPS_UNICASE = 32, + SP_CSS_FONT_VARIANT_CAPS_TITLING = 64 +}; + +// Can select more than one (see spec) +enum SPCSSFontVariantNumeric { + SP_CSS_FONT_VARIANT_NUMERIC_NORMAL = 0, + SP_CSS_FONT_VARIANT_NUMERIC_LINING_NUMS = 1, + SP_CSS_FONT_VARIANT_NUMERIC_OLDSTYLE_NUMS = 2, + SP_CSS_FONT_VARIANT_NUMERIC_PROPORTIONAL_NUMS = 4, + SP_CSS_FONT_VARIANT_NUMERIC_TABULAR_NUMS = 8, + SP_CSS_FONT_VARIANT_NUMERIC_DIAGONAL_FRACTIONS = 16, + SP_CSS_FONT_VARIANT_NUMERIC_STACKED_FRACTIONS = 32, + SP_CSS_FONT_VARIANT_NUMERIC_ORDINAL = 64, + SP_CSS_FONT_VARIANT_NUMERIC_SLASHED_ZERO = 128 +}; + +// Quite complicated... (see spec) +enum SPCSSFontVariantAlternates { + SP_CSS_FONT_VARIANT_ALTERNATES_NORMAL, + SP_CSS_FONT_VARIANT_ALTERNATES_HISTORICAL_FORMS, + SP_CSS_FONT_VARIANT_ALTERNATES_STYLISTIC, + SP_CSS_FONT_VARIANT_ALTERNATES_STYLESET, + SP_CSS_FONT_VARIANT_ALTERNATES_CHARACTER_VARIANT, + SP_CSS_FONT_VARIANT_ALTERNATES_SWASH, + SP_CSS_FONT_VARIANT_ALTERNATES_ORNAMENTS, + SP_CSS_FONT_VARIANT_ALTERNATES_ANNOTATION +}; + +// Can select more than one (see spec) +enum SPCSSFontVariantEastAsian { + SP_CSS_FONT_VARIANT_EAST_ASIAN_NORMAL = 0, + SP_CSS_FONT_VARIANT_EAST_ASIAN_JIS78 = 1, + SP_CSS_FONT_VARIANT_EAST_ASIAN_JIS83 = 2, + SP_CSS_FONT_VARIANT_EAST_ASIAN_JIS90 = 4, + SP_CSS_FONT_VARIANT_EAST_ASIAN_JIS04 = 8, + SP_CSS_FONT_VARIANT_EAST_ASIAN_SIMPLIFIED = 16, + SP_CSS_FONT_VARIANT_EAST_ASIAN_TRADITIONAL = 32, + SP_CSS_FONT_VARIANT_EAST_ASIAN_FULL_WIDTH = 64, + SP_CSS_FONT_VARIANT_EAST_ASIAN_PROPORTIONAL_WIDTH = 128, + SP_CSS_FONT_VARIANT_EAST_ASIAN_RUBY = 256 +}; + enum SPCSSTextAlign { SP_CSS_TEXT_ALIGN_START, SP_CSS_TEXT_ALIGN_END, @@ -309,6 +378,77 @@ static SPStyleEnum const enum_font_stretch[] = { {NULL, -1} }; +static SPStyleEnum const enum_font_variant_ligatures[] = { + {"none", SP_CSS_FONT_VARIANT_LIGATURES_NONE}, + {"common-ligatures", SP_CSS_FONT_VARIANT_LIGATURES_COMMON}, + {"discretionary-ligatures", SP_CSS_FONT_VARIANT_LIGATURES_DISCRETIONARY}, + {"historical-ligatures", SP_CSS_FONT_VARIANT_LIGATURES_HISTORICAL}, + {"contextual", SP_CSS_FONT_VARIANT_LIGATURES_CONTEXTUAL}, + {"normal", SP_CSS_FONT_VARIANT_LIGATURES_NORMAL}, + {"no-common-ligatures", SP_CSS_FONT_VARIANT_LIGATURES_NOCOMMON}, + {"no-discretionary-ligatures", SP_CSS_FONT_VARIANT_LIGATURES_NODISCRETIONARY}, + {"no-historical-ligatures", SP_CSS_FONT_VARIANT_LIGATURES_NOHISTORICAL}, + {"no-contextual", SP_CSS_FONT_VARIANT_LIGATURES_NOCONTEXTUAL}, + {NULL, -1} +}; + +static SPStyleEnum const enum_font_variant_position[] = { + {"normal", SP_CSS_FONT_VARIANT_POSITION_NORMAL}, + {"sub", SP_CSS_FONT_VARIANT_POSITION_SUB}, + {"super", SP_CSS_FONT_VARIANT_POSITION_SUPER}, + {NULL, -1} +}; + +static SPStyleEnum const enum_font_variant_caps[] = { + {"normal", SP_CSS_FONT_VARIANT_CAPS_NORMAL}, + {"small-caps", SP_CSS_FONT_VARIANT_CAPS_SMALL}, + {"all-small-caps", SP_CSS_FONT_VARIANT_CAPS_ALL_SMALL}, + {"petite-caps", SP_CSS_FONT_VARIANT_CAPS_PETITE}, + {"all-petite-caps", SP_CSS_FONT_VARIANT_CAPS_ALL_PETITE}, + {"unicase", SP_CSS_FONT_VARIANT_CAPS_UNICASE}, + {"titling", SP_CSS_FONT_VARIANT_CAPS_TITLING}, + {NULL, -1} +}; + +static SPStyleEnum const enum_font_variant_numeric[] = { + {"normal", SP_CSS_FONT_VARIANT_NUMERIC_NORMAL}, + {"lining-nums", SP_CSS_FONT_VARIANT_NUMERIC_LINING_NUMS}, + {"oldstyle-nums", SP_CSS_FONT_VARIANT_NUMERIC_OLDSTYLE_NUMS}, + {"proportional-nums", SP_CSS_FONT_VARIANT_NUMERIC_PROPORTIONAL_NUMS}, + {"tabular-nums", SP_CSS_FONT_VARIANT_NUMERIC_TABULAR_NUMS}, + {"diagonal-fractions", SP_CSS_FONT_VARIANT_NUMERIC_DIAGONAL_FRACTIONS}, + {"stacked-fractions", SP_CSS_FONT_VARIANT_NUMERIC_STACKED_FRACTIONS}, + {"ordinal", SP_CSS_FONT_VARIANT_NUMERIC_ORDINAL}, + {"slashed-zero", SP_CSS_FONT_VARIANT_NUMERIC_SLASHED_ZERO}, + {NULL, -1} +}; + +static SPStyleEnum const enum_font_variant_alternates[] = { + {"normal", SP_CSS_FONT_VARIANT_ALTERNATES_NORMAL}, + {"historical-forms", SP_CSS_FONT_VARIANT_ALTERNATES_HISTORICAL_FORMS}, + {"stylistic", SP_CSS_FONT_VARIANT_ALTERNATES_STYLISTIC}, + {"styleset", SP_CSS_FONT_VARIANT_ALTERNATES_STYLESET}, + {"character_variant", SP_CSS_FONT_VARIANT_ALTERNATES_CHARACTER_VARIANT}, + {"swash", SP_CSS_FONT_VARIANT_ALTERNATES_SWASH}, + {"ornaments", SP_CSS_FONT_VARIANT_ALTERNATES_ORNAMENTS}, + {"annotation", SP_CSS_FONT_VARIANT_ALTERNATES_ANNOTATION}, + {NULL, -1} +}; + +static SPStyleEnum const enum_font_variant_east_asian[] = { + {"normal", SP_CSS_FONT_VARIANT_EAST_ASIAN_NORMAL}, + {"jis78", SP_CSS_FONT_VARIANT_EAST_ASIAN_JIS78}, + {"jis83", SP_CSS_FONT_VARIANT_EAST_ASIAN_JIS83}, + {"jis90", SP_CSS_FONT_VARIANT_EAST_ASIAN_JIS90}, + {"jis04", SP_CSS_FONT_VARIANT_EAST_ASIAN_JIS04}, + {"simplified", SP_CSS_FONT_VARIANT_EAST_ASIAN_SIMPLIFIED}, + {"traditional", SP_CSS_FONT_VARIANT_EAST_ASIAN_TRADITIONAL}, + {"full-width", SP_CSS_FONT_VARIANT_EAST_ASIAN_FULL_WIDTH}, + {"proportional-width", SP_CSS_FONT_VARIANT_EAST_ASIAN_PROPORTIONAL_WIDTH}, + {"ruby", SP_CSS_FONT_VARIANT_EAST_ASIAN_RUBY}, + {NULL, -1} +}; + static SPStyleEnum const enum_text_align[] = { {"start", SP_CSS_TEXT_ALIGN_START}, {"end", SP_CSS_TEXT_ALIGN_END}, diff --git a/src/style-internal.cpp b/src/style-internal.cpp index 915282301..c117a97f9 100644 --- a/src/style-internal.cpp +++ b/src/style-internal.cpp @@ -45,6 +45,8 @@ #include <sigc++/functors/ptr_fun.h> #include <sigc++/adaptors/bind.h> +#include <glibmm/regex.h> + // TODO REMOVE OR MAKE MEMBER FUNCTIONS void sp_style_fill_paint_server_ref_changed( SPObject *old_ref, SPObject *ref, SPStyle *style); void sp_style_stroke_paint_server_ref_changed(SPObject *old_ref, SPObject *ref, SPStyle *style); @@ -664,6 +666,241 @@ SPIEnum::operator==(const SPIBase& rhs) { } +// SPIEnumBits ---------------------------------------------------------- +// Used for 'font-variant-xxx' +void +SPIEnumBits::read( gchar const *str ) { + + if( !str ) return; + std::cout << "SPIEnumBits: " << name << ": " << str << std::endl; + if( !strcmp(str, "inherit") ) { + set = true; + inherit = true; + } else { + for (unsigned i = 0; enums[i].key; i++) { + if (!strcmp(str, enums[i].key)) { + std::cout << " found: " << enums[i].key << std::endl; + set = true; + inherit = false; + value += enums[i].value; + /* Save copying for values not needing it */ + computed = value; + } + } + } +} + +const Glib::ustring +SPIEnumBits::write( guint const flags, SPIBase const *const base) const { + + SPIEnum const *const my_base = dynamic_cast<const SPIEnum*>(base); + if ( (flags & SP_STYLE_FLAG_ALWAYS) || + ((flags & SP_STYLE_FLAG_IFSET) && this->set) || + ((flags & SP_STYLE_FLAG_IFDIFF) && this->set + && (!my_base->set || this != my_base ))) + { + if (this->inherit) { + return (name + ":inherit;"); + } + if (this->value == 0 ) { + return (name + ":normal"); + } + Glib::ustring return_string = name + ":"; + unsigned j = 1; + for (unsigned i = 0; enums[i].key; ++i) { + if (j & this->value ) { + return_string += enums[i].value + " "; + } + j *= 2; + } + return return_string; + } + return Glib::ustring(""); +} + + +// SPILigatures ----------------------------------------------------- +// Used for 'font-variant-ligatures' +void +SPILigatures::read( gchar const *str ) { + + if( !str ) return; + + value = SP_CSS_FONT_VARIANT_LIGATURES_NORMAL; + if( !strcmp(str, "inherit") ) { + set = true; + inherit = true; + } else if (!strcmp(str, "normal" )) { + // Defaults for TrueType + inherit = false; + set = true; + } else if (!strcmp(str, "none" )) { + value = SP_CSS_FONT_VARIANT_LIGATURES_NONE; + inherit = false; + set = true; + } else { + // We need to parse in order + std::vector<Glib::ustring> tokens = Glib::Regex::split_simple("\\s+", str ); + for( unsigned i = 0; i < tokens.size(); ++i ) { + for (unsigned j = 0; enums[j].key; ++j ) { + if (tokens[i].compare( enums[j].key ) == 0 ) { + set = true; + inherit = false; + if( enums[j].value < SP_CSS_FONT_VARIANT_LIGATURES_NOCOMMON ) { + // Turn on + value |= enums[j].value; + } else { + // Turn off + value &= ~(enums[j].value >> 4); + } + } + } + } + } + computed = value; +} + +const Glib::ustring +SPILigatures::write( guint const flags, SPIBase const *const base) const { + + SPIEnum const *const my_base = dynamic_cast<const SPIEnum*>(base); + if ( (flags & SP_STYLE_FLAG_ALWAYS) || + ((flags & SP_STYLE_FLAG_IFSET) && this->set) || + ((flags & SP_STYLE_FLAG_IFDIFF) && this->set + && (!my_base->set || this != my_base ))) + { + if (this->inherit) { + return (name + ":inherit;"); + } + if (value == SP_CSS_FONT_VARIANT_LIGATURES_NONE ) { + return (name + ":none;"); + } + if (value == SP_CSS_FONT_VARIANT_LIGATURES_NORMAL ) { + return (name + ":normal;"); + } + + Glib::ustring return_string = name + ":"; + if ( !(value & SP_CSS_FONT_VARIANT_LIGATURES_COMMON) ) + return_string += "no-common-ligatures "; + if ( value & SP_CSS_FONT_VARIANT_LIGATURES_DISCRETIONARY ) + return_string += "discretionary-ligatures "; + if ( value & SP_CSS_FONT_VARIANT_LIGATURES_HISTORICAL ) + return_string += "historical-ligatures "; + if ( !(value & SP_CSS_FONT_VARIANT_LIGATURES_CONTEXTUAL) ) + return_string += "no-contextual "; + return_string.erase( return_string.size() - 1 ); + return_string += ";"; + return return_string; + } + return Glib::ustring(""); +} + + +// SPINumeric ----------------------------------------------------- +// Used for 'font-variant-numeric' +void +SPINumeric::read( gchar const *str ) { + + if( !str ) return; + + value = SP_CSS_FONT_VARIANT_NUMERIC_NORMAL; + if( !strcmp(str, "inherit") ) { + set = true; + inherit = true; + } else if (!strcmp(str, "normal" )) { + // Defaults for TrueType + inherit = false; + set = true; + } else { + // We need to parse in order + std::vector<Glib::ustring> tokens = Glib::Regex::split_simple("\\s+", str ); + for( unsigned i = 0; i < tokens.size(); ++i ) { + for (unsigned j = 0; enums[j].key; ++j ) { + if (tokens[i].compare( enums[j].key ) == 0 ) { + set = true; + inherit = false; + value |= enums[j].value; + + // Must switch off incompatible value + switch (enums[j].value ) { + case SP_CSS_FONT_VARIANT_NUMERIC_LINING_NUMS: + value &= ~SP_CSS_FONT_VARIANT_NUMERIC_OLDSTYLE_NUMS; + break; + case SP_CSS_FONT_VARIANT_NUMERIC_OLDSTYLE_NUMS: + value &= ~SP_CSS_FONT_VARIANT_NUMERIC_LINING_NUMS; + break; + + case SP_CSS_FONT_VARIANT_NUMERIC_PROPORTIONAL_NUMS: + value &= ~SP_CSS_FONT_VARIANT_NUMERIC_TABULAR_NUMS; + break; + case SP_CSS_FONT_VARIANT_NUMERIC_TABULAR_NUMS: + value &= ~SP_CSS_FONT_VARIANT_NUMERIC_PROPORTIONAL_NUMS; + break; + + case SP_CSS_FONT_VARIANT_NUMERIC_DIAGONAL_FRACTIONS: + value &= ~SP_CSS_FONT_VARIANT_NUMERIC_STACKED_FRACTIONS; + break; + case SP_CSS_FONT_VARIANT_NUMERIC_STACKED_FRACTIONS: + value &= ~SP_CSS_FONT_VARIANT_NUMERIC_DIAGONAL_FRACTIONS; + break; + + case SP_CSS_FONT_VARIANT_NUMERIC_NORMAL: + case SP_CSS_FONT_VARIANT_NUMERIC_ORDINAL: + case SP_CSS_FONT_VARIANT_NUMERIC_SLASHED_ZERO: + // Do nothing + break; + + default: + std::cerr << "SPINumeric::read(): Invalid value." << std::endl; + break; + } + } + } + } + } + computed = value; +} + +const Glib::ustring +SPINumeric::write( guint const flags, SPIBase const *const base) const { + + SPIEnum const *const my_base = dynamic_cast<const SPIEnum*>(base); + if ( (flags & SP_STYLE_FLAG_ALWAYS) || + ((flags & SP_STYLE_FLAG_IFSET) && this->set) || + ((flags & SP_STYLE_FLAG_IFDIFF) && this->set + && (!my_base->set || this != my_base ))) + { + if (this->inherit) { + return (name + ":inherit;"); + } + if (value == SP_CSS_FONT_VARIANT_NUMERIC_NORMAL ) { + return (name + ":normal;"); + } + + Glib::ustring return_string = name + ":"; + if ( value & SP_CSS_FONT_VARIANT_NUMERIC_LINING_NUMS ) + return_string += "lining-nums "; + if ( value & SP_CSS_FONT_VARIANT_NUMERIC_OLDSTYLE_NUMS ) + return_string += "oldstyle-nums "; + if ( value & SP_CSS_FONT_VARIANT_NUMERIC_PROPORTIONAL_NUMS ) + return_string += "proportional-nums "; + if ( value & SP_CSS_FONT_VARIANT_NUMERIC_TABULAR_NUMS ) + return_string += "tabular-nums "; + if ( value & SP_CSS_FONT_VARIANT_NUMERIC_DIAGONAL_FRACTIONS ) + return_string += "diagonal-fractions "; + if ( value & SP_CSS_FONT_VARIANT_NUMERIC_STACKED_FRACTIONS ) + return_string += "stacked-fractions "; + if ( value & SP_CSS_FONT_VARIANT_NUMERIC_ORDINAL ) + return_string += "ordinal "; + if ( value & SP_CSS_FONT_VARIANT_NUMERIC_SLASHED_ZERO ) + return_string += "slashed-zero "; + return_string.erase( return_string.size() - 1 ); + return_string += ";"; + return return_string; + } + return Glib::ustring(""); +} + // SPIString ------------------------------------------------------------ @@ -1694,6 +1931,11 @@ SPIFontSize::read( gchar const *str ) { unit = length.unit; value = length.value; computed = length.computed; + /* Set a minimum font size to something much smaller than should ever (ever!) be encountered in a real file. + If a bad SVG file is encountered and this is zero odd things + might happen because the inverse is used in some scaling actions. + */ + if ( computed <= 1.0e-32 ) { computed = 1.0e-32; } if( unit == SP_CSS_UNIT_PERCENT ) { type = SP_FONT_SIZE_PERCENTAGE; } else { @@ -1774,6 +2016,11 @@ SPIFontSize::cascade( const SPIBase* const parent ) { break; } } + /* Set a minimum font size to something much smaller than should ever (ever!) be encountered in a real file. + If a bad SVG file is encountered and this is zero odd things + might happen because the inverse is used in some scaling actions. + */ + if ( computed <= 1.0e-32 ) { computed = 1.0e-32; } } else { std::cerr << "SPIFontSize::cascade(): Incorrect parent type" << std::endl; } @@ -1862,6 +2109,11 @@ SPIFontSize::merge( const SPIBase* const parent ) { } } } // Relative size + /* Set a minimum font size to something much smaller than should ever (ever!) be encountered in a real file. + If a bad SVG file is encountered and this is zero odd things + might happen because the inverse is used in some scaling actions. + */ + if ( computed <= 1.0e-32 ) { computed = 1.0e-32; } } // Parent set and not inherit } else { std::cerr << "SPIFontSize::merge(): Incorrect parent type" << std::endl; diff --git a/src/style-internal.h b/src/style-internal.h index a8f0c5096..1ddab30f1 100644 --- a/src/style-internal.h +++ b/src/style-internal.h @@ -501,12 +501,83 @@ public: public: SPStyleEnum const *enums; - unsigned value : 8; - unsigned computed: 8; + unsigned value : 16; // 9 bits required for 'font-variant-east-asian' + unsigned computed: 16; private: - unsigned value_default : 8; - unsigned computed_default: 8; // for font-weight + unsigned value_default : 16; + unsigned computed_default: 16; // for font-weight +}; + + +/// SPIEnum w/ bits, allows values with multiple key words. +class SPIEnumBits : public SPIEnum +{ + +public: + SPIEnumBits() : + SPIEnum( "anonymous_enumbits", NULL ) + {} + + SPIEnumBits( Glib::ustring const &name, SPStyleEnum const *enums, unsigned value = 0, bool inherits = true ) : + SPIEnum( name, enums, value, inherit ) + {} + + virtual ~SPIEnumBits() + {} + + virtual void read( gchar const *str ); + virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, + SPIBase const *const base = NULL ) const; + +}; + + +/// SPIEnum w/ extra bits. The 'font-variants-ligatures' property is a complete mess that needs +/// special handling. For OpenType fonts the values 'common-ligatures', 'contextual', +/// 'no-discretionary-ligatures', and 'no-historical-ligatures' are not useful but we still must be +/// able to parse them. +class SPILigatures : public SPIEnum +{ + +public: + SPILigatures() : + SPIEnum( "anonymous_enumligatures", NULL ) + {} + + SPILigatures( Glib::ustring const &name, SPStyleEnum const *enums) : + SPIEnum( name, enums, SP_CSS_FONT_VARIANT_LIGATURES_NORMAL ) + {} + + virtual ~SPILigatures() + {} + + virtual void read( gchar const *str ); + virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, + SPIBase const *const base = NULL ) const; +}; + + +/// SPIEnum w/ extra bits. The 'font-variants-numeric' property is a complete mess that needs +/// special handling. Multiple key words can be specified, some exclusive of others. +class SPINumeric : public SPIEnum +{ + +public: + SPINumeric() : + SPIEnum( "anonymous_enumnumeric", NULL ) + {} + + SPINumeric( Glib::ustring const &name, SPStyleEnum const *enums) : + SPIEnum( name, enums, SP_CSS_FONT_VARIANT_NUMERIC_NORMAL ) + {} + + virtual ~SPINumeric() + {} + + virtual void read( gchar const *str ); + virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, + SPIBase const *const base = NULL ) const; }; diff --git a/src/style-test.h b/src/style-test.h index cd6769b24..c6bb665e0 100644 --- a/src/style-test.h +++ b/src/style-test.h @@ -109,13 +109,13 @@ public: TestCase("font: 12pt/15pt sans-serif", "font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:15pt;font-family:sans-serif"), TestCase("font: 80% sans-serif", - "font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:80.00000119%;line-height:normal;font-family:sans-serif"), + "font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:80%;line-height:normal;font-family:sans-serif"), TestCase("font: x-large/110% 'new century schoolbook', serif", - "font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:x-large;line-height:110.00000238%;font-family:\'new century schoolbook\', serif"), + "font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:x-large;line-height:110%;font-family:\'new century schoolbook\', serif"), TestCase("font: bold italic large Palatino, serif", "font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:large;line-height:normal;font-family:Palatino, serif"), TestCase("font: normal small-caps 120%/120% fantasy", - "font-style:normal;font-variant:small-caps;font-weight:normal;font-stretch:normal;font-size:120.00000477%;line-height:120.00000477%;font-family:fantasy"), + "font-style:normal;font-variant:small-caps;font-weight:normal;font-stretch:normal;font-size:120%;line-height:120%;font-family:fantasy"), TestCase("font: condensed oblique 12pt 'Helvetica Neue', serif;", "font-style:oblique;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:16px;line-height:normal;font-family:\'Helvetica Neue\', serif"), @@ -137,6 +137,34 @@ public: TestCase("font-weight:bolder"), TestCase("font-stretch:condensed"), // SPIEnum + TestCase("font-variant-ligatures:none"), // SPILigatures + TestCase("font-variant-ligatures:normal"), + TestCase("font-variant-ligatures:no-common-ligatures"), + TestCase("font-variant-ligatures:discretionary-ligatures"), + TestCase("font-variant-ligatures:historical-ligatures"), + TestCase("font-variant-ligatures:no-contextual"), + TestCase("font-variant-ligatures:common-ligatures", "font-variant-ligatures:normal"), + TestCase("font-variant-ligatures:contextual", "font-variant-ligatures:normal"), + TestCase("font-variant-ligatures:no-common-ligatures historical-ligatures"), + TestCase("font-variant-ligatures:historical-ligatures no-contextual"), + TestCase("font-variant-position:normal"), + TestCase("font-variant-position:sub"), + TestCase("font-variant-position:super"), + TestCase("font-variant-caps:normal"), + TestCase("font-variant-caps:small-caps"), + TestCase("font-variant-caps:all-small-caps"), + TestCase("font-variant-numeric:normal"), + TestCase("font-variant-numeric:lining-nums"), + TestCase("font-variant-numeric:oldstyle-nums"), + TestCase("font-variant-numeric:proportional-nums"), + TestCase("font-variant-numeric:tabular-nums"), + TestCase("font-variant-numeric:diagonal-fractions"), + TestCase("font-variant-numeric:stacked-fractions"), + TestCase("font-variant-numeric:ordinal"), + TestCase("font-variant-numeric:slashed-zero"), + TestCase("font-variant-numeric:tabular-nums slashed-zero"), + TestCase("font-variant-numeric:tabular-nums proportional-nums", "font-variant-numeric:proportional-nums"), + // Should be moved down TestCase("text-indent:12em"), // SPILength? TestCase("text-align:center"), // SPIEnum diff --git a/src/style.cpp b/src/style.cpp index b65bff53f..d8402e08a 100644 --- a/src/style.cpp +++ b/src/style.cpp @@ -14,7 +14,7 @@ * Copyright (C) 2001 Ximian, Inc. * Copyright (C) 2005 Monash University * Copyright (C) 2012 Kris De Gussem - * Copyright (C) 2014 Tavmjong Bah + * Copyright (C) 2014-2015 Tavmjong Bah * * Released under GNU GPL, read the file 'COPYING' for more information */ @@ -118,6 +118,15 @@ SPStyle::SPStyle(SPDocument *document_in, SPObject *object_in) : font(), // SPIFont font_specification( "-inkscape-font-specification" ), // SPIString + // Font variants + font_variant_ligatures( "font-variant-ligatures", enum_font_variant_ligatures ), + font_variant_position( "font-variant-position", enum_font_variant_position, SP_CSS_FONT_VARIANT_POSITION_NORMAL ), + font_variant_caps( "font-variant-caps", enum_font_variant_caps, SP_CSS_FONT_VARIANT_CAPS_NORMAL ), + font_variant_numeric( "font-variant-numeric", enum_font_variant_numeric ), + font_variant_alternates("font-variant-alternates", enum_font_variant_alternates, SP_CSS_FONT_VARIANT_ALTERNATES_NORMAL ), + font_variant_east_asian("font-variant-east_asian", enum_font_variant_east_asian, SP_CSS_FONT_VARIANT_EAST_ASIAN_NORMAL ), + font_feature_settings( "font-feature-settings", "normal" ), + // Text related properties text_indent( "text-indent", 0.0 ), // SPILength text_align( "text-align", enum_text_align, SP_CSS_TEXT_ALIGN_START ), @@ -288,6 +297,15 @@ SPStyle::SPStyle(SPDocument *document_in, SPObject *object_in) : _properties.push_back( &font ); _properties.push_back( &font_specification ); + // Font variants + _properties.push_back( &font_variant_ligatures ); + _properties.push_back( &font_variant_position ); + _properties.push_back( &font_variant_caps ); + _properties.push_back( &font_variant_numeric ); + _properties.push_back( &font_variant_alternates ); + _properties.push_back( &font_variant_east_asian ); + _properties.push_back( &font_feature_settings ); + _properties.push_back( &text_indent ); _properties.push_back( &text_align ); @@ -374,6 +392,14 @@ SPStyle::SPStyle(SPDocument *document_in, SPObject *object_in) : // _propmap.insert( std::make_pair( font.name, reinterpret_cast<SPIBasePtr>(&SPStyle::font ) ) ); // _propmap.insert( std::make_pair( font_specification.name, reinterpret_cast<SPIBasePtr>(&SPStyle::font_specification ) ) ); + // font_variant_ligatures ); + // font_variant_position ); + // font_variant_caps ); + // font_variant_numeric ); + // font_variant_alternates ); + // font_variant_east_asian ); + // font_feature_settings ); + // _propmap.insert( std::make_pair( text_indent.name, reinterpret_cast<SPIBasePtr>(&SPStyle::text_indent ) ) ); // _propmap.insert( std::make_pair( text_align.name, reinterpret_cast<SPIBasePtr>(&SPStyle::text_align ) ) ); @@ -573,7 +599,15 @@ SPStyle::read( SPObject *object, Inkscape::XML::Node *repr ) { /* 3 Presentation attributes */ // std::cout << " MERGING PRESENTATION ATTRIBUTES" << std::endl; for(std::vector<SPIBase*>::size_type i = 0; i != _properties.size(); ++i) { - _properties[i]->readAttribute( repr ); + + // Shorthands are not allowed as presentation properites. Note: text-decoration and + // font-variant are converted to shorthands in CSS 3 but can still be read as a + // non-shorthand for compatability with older renders, so they should not be in this list. + // We could add a flag to SPIBase to avoid string comparison. + if( _properties[i]->name.compare( "font" ) != 0 && + _properties[i]->name.compare( "marker" ) != 0 ) { + _properties[i]->readAttribute( repr ); + } } // for(SPPropMap::iterator i = _propmap.begin(); i != _propmap.end(); ++i ) { // (this->*(i->second)).readAttribute( repr ); @@ -686,6 +720,29 @@ SPStyle::readIfUnset( gint id, gchar const *val ) { font.readIfUnset( val ); break; + /* Font Variants CSS 3 */ + case SP_PROP_FONT_VARIANT_LIGATURES: + font_variant_ligatures.readIfUnset( val ); + break; + case SP_PROP_FONT_VARIANT_POSITION: + font_variant_position.readIfUnset( val ); + break; + case SP_PROP_FONT_VARIANT_CAPS: + font_variant_caps.readIfUnset( val ); + break; + case SP_PROP_FONT_VARIANT_NUMERIC: + font_variant_numeric.readIfUnset( val ); + break; + case SP_PROP_FONT_VARIANT_ALTERNATES: + font_variant_alternates.readIfUnset( val ); + break; + case SP_PROP_FONT_VARIANT_EAST_ASIAN: + font_variant_east_asian.readIfUnset( val ); + break; + case SP_PROP_FONT_FEATURE_SETTINGS: + font_feature_settings.readIfUnset( val ); + break; + /* Text */ case SP_PROP_TEXT_INDENT: text_indent.readIfUnset( val ); @@ -1133,6 +1190,92 @@ SPStyle::_mergeObjectStylesheet( SPObject const *const object ) { } } +std::string +SPStyle::getFontFeatureString() { + + std::string feature_string; + + if ( !(font_variant_ligatures.value & SP_CSS_FONT_VARIANT_LIGATURES_COMMON) ) + feature_string += "liga 0, clig 0, "; + if ( font_variant_ligatures.value & SP_CSS_FONT_VARIANT_LIGATURES_DISCRETIONARY ) + feature_string += "dlig, "; + if ( font_variant_ligatures.value & SP_CSS_FONT_VARIANT_LIGATURES_HISTORICAL ) + feature_string += "hlig, "; + if ( !(font_variant_ligatures.value & SP_CSS_FONT_VARIANT_LIGATURES_CONTEXTUAL) ) + feature_string += "calt 0, "; + + if ( font_variant_position.value & SP_CSS_FONT_VARIANT_POSITION_SUB ) + feature_string += "subs, "; + if ( font_variant_position.value & SP_CSS_FONT_VARIANT_POSITION_SUPER ) + feature_string += "sups, "; + + if ( font_variant_caps.value & SP_CSS_FONT_VARIANT_CAPS_SMALL ) + feature_string += "smcp, "; + if ( font_variant_caps.value & SP_CSS_FONT_VARIANT_CAPS_ALL_SMALL ) + feature_string += "smcp, c2sc, "; + if ( font_variant_caps.value & SP_CSS_FONT_VARIANT_CAPS_PETITE ) + feature_string += "pcap, "; + if ( font_variant_caps.value & SP_CSS_FONT_VARIANT_CAPS_ALL_PETITE ) + feature_string += "pcap, c2pc, "; + if ( font_variant_caps.value & SP_CSS_FONT_VARIANT_CAPS_UNICASE ) + feature_string += "unic, "; + if ( font_variant_caps.value & SP_CSS_FONT_VARIANT_CAPS_TITLING ) + feature_string += "titl, "; + + if ( font_variant_numeric.value & SP_CSS_FONT_VARIANT_NUMERIC_LINING_NUMS ) + feature_string += "lnum, "; + if ( font_variant_numeric.value & SP_CSS_FONT_VARIANT_NUMERIC_OLDSTYLE_NUMS ) + feature_string += "onum, "; + if ( font_variant_numeric.value & SP_CSS_FONT_VARIANT_NUMERIC_PROPORTIONAL_NUMS ) + feature_string += "pnum, "; + if ( font_variant_numeric.value & SP_CSS_FONT_VARIANT_NUMERIC_TABULAR_NUMS ) + feature_string += "tnum, "; + if ( font_variant_numeric.value & SP_CSS_FONT_VARIANT_NUMERIC_DIAGONAL_FRACTIONS ) + feature_string += "frac, "; + if ( font_variant_numeric.value & SP_CSS_FONT_VARIANT_NUMERIC_STACKED_FRACTIONS ) + feature_string += "afrc, "; + if ( font_variant_numeric.value & SP_CSS_FONT_VARIANT_NUMERIC_ORDINAL ) + feature_string += "ordn, "; + if ( font_variant_numeric.value & SP_CSS_FONT_VARIANT_NUMERIC_SLASHED_ZERO ) + feature_string += "zero, "; + + if( font_variant_east_asian.value & SP_CSS_FONT_VARIANT_EAST_ASIAN_JIS78 ) + feature_string += "jp78, "; + if( font_variant_east_asian.value & SP_CSS_FONT_VARIANT_EAST_ASIAN_JIS83 ) + feature_string += "jp83, "; + if( font_variant_east_asian.value & SP_CSS_FONT_VARIANT_EAST_ASIAN_JIS90 ) + feature_string += "jp90, "; + if( font_variant_east_asian.value & SP_CSS_FONT_VARIANT_EAST_ASIAN_JIS04 ) + feature_string += "jp04, "; + if( font_variant_east_asian.value & SP_CSS_FONT_VARIANT_EAST_ASIAN_SIMPLIFIED ) + feature_string += "smpl, "; + if( font_variant_east_asian.value & SP_CSS_FONT_VARIANT_EAST_ASIAN_TRADITIONAL ) + feature_string += "trad, "; + if( font_variant_east_asian.value & SP_CSS_FONT_VARIANT_EAST_ASIAN_FULL_WIDTH ) + feature_string += "fwid, "; + if( font_variant_east_asian.value & SP_CSS_FONT_VARIANT_EAST_ASIAN_PROPORTIONAL_WIDTH ) + feature_string += "pwid, "; + if( font_variant_east_asian.value & SP_CSS_FONT_VARIANT_EAST_ASIAN_RUBY ) + feature_string += "ruby, "; + + if ( strcmp( font_feature_settings.value, "normal") ) { + // We do no sanity checking... + feature_string += font_feature_settings.value; + feature_string += ", "; + } + + if (feature_string.empty()) { + feature_string = "normal"; + } else { + // Remove last ", " + feature_string.erase( feature_string.size() - 1 ); + feature_string.erase( feature_string.size() - 1 ); + } + + return feature_string; +} + + // Internal /** * Release callback. @@ -1169,10 +1312,12 @@ void sp_style_filter_ref_changed(SPObject *old_ref, SPObject *ref, SPStyle *style) { if (old_ref) { + (dynamic_cast<SPFilter *>( old_ref ))->_refcount--; style->filter_modified_connection.disconnect(); } if ( SP_IS_FILTER(ref)) { + (dynamic_cast<SPFilter *>( ref ))->_refcount++; style->filter_modified_connection = ref->connectModified(sigc::bind(sigc::ptr_fun(&sp_style_filter_ref_modified), style)); } @@ -1652,6 +1797,14 @@ sp_css_attr_unset_text(SPCSSAttr *css) sp_repr_css_set_property(css, "text-decoration-color", NULL); sp_repr_css_set_property(css, "text-decoration-style", NULL); + sp_repr_css_set_property(css, "font-variant-ligatures", NULL); + sp_repr_css_set_property(css, "font-variant-position", NULL); + sp_repr_css_set_property(css, "font-variant-caps", NULL); + sp_repr_css_set_property(css, "font-variant-numeric", NULL); + sp_repr_css_set_property(css, "font-variant-alternates", NULL); + sp_repr_css_set_property(css, "font-variant-east-asian", NULL); + sp_repr_css_set_property(css, "font-feature-settings", NULL); + return css; } diff --git a/src/style.h b/src/style.h index ab34476b3..02432d3e7 100644 --- a/src/style.h +++ b/src/style.h @@ -94,7 +94,7 @@ public: /** Font style */ SPIEnum font_style; - /** Which substyle of the font */ + /** Which substyle of the font (CSS 2. CSS 3 redefines as shorthand) */ SPIEnum font_variant; /** Weight of the font */ SPIEnum font_weight; @@ -111,6 +111,22 @@ public: /** Full font name, as font_factory::ConstructFontSpecification would give, for internal use. */ SPIString font_specification; + /* Font variants -------------------- */ + /** Font variant ligatures */ + SPILigatures font_variant_ligatures; + /** Font variant position (subscript/superscript) */ + SPIEnum font_variant_position; + /** Font variant caps (small caps) */ + SPIEnum font_variant_caps; + /** Font variant numeric (numerical formatting) */ + SPINumeric font_variant_numeric; + /** Font variant alternates (alternates/swatches) */ + SPIEnum font_variant_alternates; + /** Font variant East Asian */ + SPIEnum font_variant_east_asian; + /** Font feature settings (Low level access to TrueType tables) */ + SPIString font_feature_settings; + /* Text ----------------------------- */ /** First line indent of paragraphs (css2 16.1) */ @@ -287,6 +303,12 @@ public: SPPaintServer *getStrokePaintServer() { return (stroke.value.href) ? stroke.value.href->getObject() : NULL; } SPPaintServer const *getStrokePaintServer() const { return (stroke.value.href) ? stroke.value.href->getObject() : NULL; } char const *getStrokeURI() const { return (stroke.value.href) ? stroke.value.href->getURI()->toString() : NULL; } + + /** + * Return a font feature string useful for Pango. + */ + std::string getFontFeatureString(); + }; SPStyle *sp_style_ref(SPStyle *style); // SPStyle::ref(); diff --git a/src/svg/svg-path-geom-test.h b/src/svg/svg-path-geom-test.h index 3558b4e55..c3972133d 100644 --- a/src/svg/svg-path-geom-test.h +++ b/src/svg/svg-path-geom-test.h @@ -453,38 +453,6 @@ private: return false; } } - else if(Geom::HLineSegment const *la = dynamic_cast<Geom::HLineSegment const*>(ca)) - { - Geom::HLineSegment const *lb = dynamic_cast<Geom::HLineSegment const*>(cb); - if (!Geom::are_near((*la).initialPoint(),(*lb).initialPoint(), eps)) { - char temp[200]; - sprintf(temp, "Different start of segment: (%g,%g) != (%g,%g), subpath: %u, segment: %u", (*la).initialPoint()[Geom::X], (*la).initialPoint()[Geom::Y], (*lb).initialPoint()[Geom::X], (*lb).initialPoint()[Geom::Y], static_cast<unsigned int>(i), static_cast<unsigned int>(j)); - TS_FAIL(temp); - return false; - } - if (!Geom::are_near((*la).finalPoint(),(*lb).finalPoint(), eps)) { - char temp[200]; - sprintf(temp, "Different end of segment: (%g,%g) != (%g,%g), subpath: %u, segment: %u", (*la).finalPoint()[Geom::X], (*la).finalPoint()[Geom::Y], (*lb).finalPoint()[Geom::X], (*lb).finalPoint()[Geom::Y], static_cast<unsigned int>(i), static_cast<unsigned int>(j)); - TS_FAIL(temp); - return false; - } - } - else if(Geom::VLineSegment const *la = dynamic_cast<Geom::VLineSegment const*>(ca)) - { - Geom::VLineSegment const *lb = dynamic_cast<Geom::VLineSegment const*>(cb); - if (!Geom::are_near((*la).initialPoint(),(*lb).initialPoint(), eps)) { - char temp[200]; - sprintf(temp, "Different start of segment: (%g,%g) != (%g,%g), subpath: %u, segment: %u", (*la).initialPoint()[Geom::X], (*la).initialPoint()[Geom::Y], (*lb).initialPoint()[Geom::X], (*lb).initialPoint()[Geom::Y], static_cast<unsigned int>(i), static_cast<unsigned int>(j)); - TS_FAIL(temp); - return false; - } - if (!Geom::are_near((*la).finalPoint(),(*lb).finalPoint(), eps)) { - char temp[200]; - sprintf(temp, "Different end of segment: (%g,%g) != (%g,%g), subpath: %u, segment: %u", (*la).finalPoint()[Geom::X], (*la).finalPoint()[Geom::Y], (*lb).finalPoint()[Geom::X], (*lb).finalPoint()[Geom::Y], static_cast<unsigned int>(i), static_cast<unsigned int>(j)); - TS_FAIL(temp); - return false; - } - } else if(Geom::CubicBezier const *la = dynamic_cast<Geom::CubicBezier const*>(ca)) { Geom::CubicBezier const *lb = dynamic_cast<Geom::CubicBezier const*>(cb); @@ -522,91 +490,10 @@ private: } else // not same type { - if(Geom::LineSegment const *la = dynamic_cast<Geom::LineSegment const*>(ca)) - { - if (Geom::HLineSegment const *lb = dynamic_cast<Geom::HLineSegment const*>(cb)) { - if (!Geom::are_near((*la).initialPoint(),(*lb).initialPoint(), eps)) { - char temp[200]; - sprintf(temp, "Different start of segment: (%g,%g) != (%g,%g), subpath: %u, segment: %u", (*la).initialPoint()[Geom::X], (*la).initialPoint()[Geom::Y], (*lb).initialPoint()[Geom::X], (*lb).initialPoint()[Geom::Y], static_cast<unsigned int>(i), static_cast<unsigned int>(j)); - TS_FAIL(temp); - return false; - } - if (!Geom::are_near((*la).finalPoint(),(*lb).finalPoint(), eps)) { - char temp[200]; - sprintf(temp, "Different end of segment: (%g,%g) != (%g,%g), subpath: %u, segment: %u", (*la).finalPoint()[Geom::X], (*la).finalPoint()[Geom::Y], (*lb).finalPoint()[Geom::X], (*lb).finalPoint()[Geom::Y], static_cast<unsigned int>(i), static_cast<unsigned int>(j)); - TS_FAIL(temp); - return false; - } - char temp[200]; - sprintf(temp, "A LineSegment and an HLineSegment have been considered equal. Subpath: %u, segment: %u", static_cast<unsigned int>(i), static_cast<unsigned int>(j)); - TS_TRACE(temp); - } else if (Geom::VLineSegment const *lb = dynamic_cast<Geom::VLineSegment const*>(cb)) { - if (!Geom::are_near((*la).initialPoint(),(*lb).initialPoint(), eps)) { - char temp[200]; - sprintf(temp, "Different start of segment: (%g,%g) != (%g,%g), subpath: %u, segment: %u", (*la).initialPoint()[Geom::X], (*la).initialPoint()[Geom::Y], (*lb).initialPoint()[Geom::X], (*lb).initialPoint()[Geom::Y], static_cast<unsigned int>(i), static_cast<unsigned int>(j)); - TS_FAIL(temp); - return false; - } - if (!Geom::are_near((*la).finalPoint(),(*lb).finalPoint(), eps)) { - char temp[200]; - sprintf(temp, "Different end of segment: (%g,%g) != (%g,%g), subpath: %u, segment: %u", (*la).finalPoint()[Geom::X], (*la).finalPoint()[Geom::Y], (*lb).finalPoint()[Geom::X], (*lb).finalPoint()[Geom::Y], static_cast<unsigned int>(i), static_cast<unsigned int>(j)); - TS_FAIL(temp); - return false; - } - char temp[200]; - sprintf(temp, "A LineSegment and a VLineSegment have been considered equal. Subpath: %u, segment: %u", static_cast<unsigned int>(i), static_cast<unsigned int>(j)); - TS_TRACE(temp); - } else { - char temp[200]; - sprintf(temp, "Different curve types: %s != %s, subpath: %u, segment: %u", typeid(*ca).name(), typeid(*cb).name(), static_cast<unsigned int>(i), static_cast<unsigned int>(j)); - TS_FAIL(temp); - } - } - else if(Geom::LineSegment const *lb = dynamic_cast<Geom::LineSegment const*>(cb)) - { - if (Geom::HLineSegment const *la = dynamic_cast<Geom::HLineSegment const*>(ca)) { - if (!Geom::are_near((*la).initialPoint(),(*lb).initialPoint(), eps)) { - char temp[200]; - sprintf(temp, "Different start of segment: (%g,%g) != (%g,%g), subpath: %u, segment: %u", (*la).initialPoint()[Geom::X], (*la).initialPoint()[Geom::Y], (*lb).initialPoint()[Geom::X], (*lb).initialPoint()[Geom::Y], static_cast<unsigned int>(i), static_cast<unsigned int>(j)); - TS_FAIL(temp); - return false; - } - if (!Geom::are_near((*la).finalPoint(),(*lb).finalPoint(), eps)) { - char temp[200]; - sprintf(temp, "Different end of segment: (%g,%g) != (%g,%g), subpath: %u, segment: %u", (*la).finalPoint()[Geom::X], (*la).finalPoint()[Geom::Y], (*lb).finalPoint()[Geom::X], (*lb).finalPoint()[Geom::Y], static_cast<unsigned int>(i), static_cast<unsigned int>(j)); - TS_FAIL(temp); - return false; - } - char temp[200]; - sprintf(temp, "An HLineSegment and a LineSegment have been considered equal. Subpath: %u, segment: %u", static_cast<unsigned int>(i), static_cast<unsigned int>(j)); - TS_TRACE(temp); - } else if (Geom::VLineSegment const *la = dynamic_cast<Geom::VLineSegment const*>(ca)) { - if (!Geom::are_near((*la).initialPoint(),(*lb).initialPoint(), eps)) { - char temp[200]; - sprintf(temp, "Different start of segment: (%g,%g) != (%g,%g), subpath: %u, segment: %u", (*la).initialPoint()[Geom::X], (*la).initialPoint()[Geom::Y], (*lb).initialPoint()[Geom::X], (*lb).initialPoint()[Geom::Y], static_cast<unsigned int>(i), static_cast<unsigned int>(j)); - TS_FAIL(temp); - return false; - } - if (!Geom::are_near((*la).finalPoint(),(*lb).finalPoint(), eps)) { - char temp[200]; - sprintf(temp, "Different end of segment: (%g,%g) != (%g,%g), subpath: %u, segment: %u", (*la).finalPoint()[Geom::X], (*la).finalPoint()[Geom::Y], (*lb).finalPoint()[Geom::X], (*lb).finalPoint()[Geom::Y], static_cast<unsigned int>(i), static_cast<unsigned int>(j)); - TS_FAIL(temp); - return false; - } - char temp[200]; - sprintf(temp, "A VLineSegment and a LineSegment have been considered equal. Subpath: %u, segment: %u", static_cast<unsigned int>(i), static_cast<unsigned int>(j)); - TS_TRACE(temp); - } else { - char temp[200]; - sprintf(temp, "Different curve types: %s != %s, subpath: %u, segment: %u", typeid(*ca).name(), typeid(*cb).name(), static_cast<unsigned int>(i), static_cast<unsigned int>(j)); - TS_FAIL(temp); - return false; - } - } else { - char temp[200]; - sprintf(temp, "Different curve types: %s != %s, subpath: %u, segment: %u", typeid(*ca).name(), typeid(*cb).name(), static_cast<unsigned int>(i), static_cast<unsigned int>(j)); - TS_FAIL(temp); - } + char temp[200]; + sprintf(temp, "Different curve types: %s != %s, subpath: %u, segment: %u", typeid(*ca).name(), typeid(*cb).name(), static_cast<unsigned int>(i), static_cast<unsigned int>(j)); + TS_FAIL(temp); + return false; } } } diff --git a/src/svg/svg-path.cpp b/src/svg/svg-path.cpp index 9ba3c0ebd..ba9e11452 100644 --- a/src/svg/svg-path.cpp +++ b/src/svg/svg-path.cpp @@ -43,16 +43,15 @@ Geom::PathVector sp_svg_read_pathv(char const * str) if (!str) return pathv; // return empty pathvector when str == NULL - - typedef std::back_insert_iterator<Geom::PathVector> Inserter; - Inserter iter(pathv); - Geom::PathIteratorSink<Inserter> generator(iter); + Geom::PathBuilder builder(pathv); + Geom::SVGPathParser parser(builder); + parser.setZSnapThreshold(Geom::EPSILON); try { - Geom::parse_svg_path(str, generator); + parser.parse(str); } catch (Geom::SVGPathParseError &e) { - generator.flush(); + builder.flush(); // This warning is extremely annoying when testing //g_warning("Malformed SVG path, truncated path up to where error was found.\n Input path=\"%s\"\n Parsed path=\"%s\"", str, sp_svg_write_path(pathv)); } @@ -61,10 +60,17 @@ Geom::PathVector sp_svg_read_pathv(char const * str) } static void sp_svg_write_curve(Inkscape::SVG::PathString & str, Geom::Curve const * c) { + // TODO: this code needs to removed and replaced by appropriate path sink if(Geom::LineSegment const *line_segment = dynamic_cast<Geom::LineSegment const *>(c)) { // don't serialize stitch segments if (!dynamic_cast<Geom::Path::StitchSegment const *>(c)) { - str.lineTo( (*line_segment)[1][0], (*line_segment)[1][1] ); + if (line_segment->initialPoint()[Geom::X] == line_segment->finalPoint()[Geom::X]) { + str.verticalLineTo( line_segment->finalPoint()[Geom::Y] ); + } else if (line_segment->initialPoint()[Geom::Y] == line_segment->finalPoint()[Geom::Y]) { + str.horizontalLineTo( line_segment->finalPoint()[Geom::X] ); + } else { + str.lineTo( (*line_segment)[1][0], (*line_segment)[1][1] ); + } } } else if(Geom::QuadraticBezier const *quadratic_bezier = dynamic_cast<Geom::QuadraticBezier const *>(c)) { @@ -76,17 +82,11 @@ static void sp_svg_write_curve(Inkscape::SVG::PathString & str, Geom::Curve cons (*cubic_bezier)[2][0], (*cubic_bezier)[2][1], (*cubic_bezier)[3][0], (*cubic_bezier)[3][1] ); } - else if(Geom::SVGEllipticalArc const *svg_elliptical_arc = dynamic_cast<Geom::SVGEllipticalArc const *>(c)) { - str.arcTo( svg_elliptical_arc->ray(Geom::X), svg_elliptical_arc->ray(Geom::Y), - Geom::rad_to_deg(svg_elliptical_arc->rotationAngle()), - svg_elliptical_arc->largeArc(), svg_elliptical_arc->sweep(), - svg_elliptical_arc->finalPoint() ); - } - else if(Geom::HLineSegment const *hline_segment = dynamic_cast<Geom::HLineSegment const *>(c)) { - str.horizontalLineTo( hline_segment->finalPoint()[0] ); - } - else if(Geom::VLineSegment const *vline_segment = dynamic_cast<Geom::VLineSegment const *>(c)) { - str.verticalLineTo( vline_segment->finalPoint()[1] ); + else if(Geom::EllipticalArc const *elliptical_arc = dynamic_cast<Geom::EllipticalArc const *>(c)) { + str.arcTo( elliptical_arc->ray(Geom::X), elliptical_arc->ray(Geom::Y), + Geom::rad_to_deg(elliptical_arc->rotationAngle()), + elliptical_arc->largeArc(), elliptical_arc->sweep(), + elliptical_arc->finalPoint() ); } else { //this case handles sbasis as well as all other curve types Geom::Path sbasis_path = Geom::cubicbezierpath_from_sbasis(c->toSBasis(), 0.1); diff --git a/src/text-chemistry.cpp b/src/text-chemistry.cpp index 65b59f2ad..9fc862ad2 100644 --- a/src/text-chemistry.cpp +++ b/src/text-chemistry.cpp @@ -43,11 +43,10 @@ using Inkscape::DocumentUndo; static SPItem * flowtext_in_selection(Inkscape::Selection *selection) { - for (GSList *items = (GSList *) selection->itemList(); - items != NULL; - items = items->next) { - if (SP_IS_FLOWTEXT(items->data)) - return ((SPItem *) items->data); + std::vector<SPItem*> items = selection->itemList(); + for(std::vector<SPItem*>::const_iterator i=items.begin();i!=items.end();i++){ + if (SP_IS_FLOWTEXT(*i)) + return *i; } return NULL; } @@ -55,11 +54,10 @@ flowtext_in_selection(Inkscape::Selection *selection) static SPItem * text_or_flowtext_in_selection(Inkscape::Selection *selection) { - for (GSList *items = (GSList *) selection->itemList(); - items != NULL; - items = items->next) { - if (SP_IS_TEXT(items->data) || SP_IS_FLOWTEXT(items->data)) - return ((SPItem *) items->data); + std::vector<SPItem*> items = selection->itemList(); + for(std::vector<SPItem*>::const_iterator i=items.begin();i!=items.end();i++){ + if (SP_IS_TEXT(*i) || SP_IS_FLOWTEXT(*i)) + return *i; } return NULL; } @@ -67,11 +65,10 @@ text_or_flowtext_in_selection(Inkscape::Selection *selection) static SPItem * shape_in_selection(Inkscape::Selection *selection) { - for (GSList *items = (GSList *) selection->itemList(); - items != NULL; - items = items->next) { - if (SP_IS_SHAPE(items->data)) - return ((SPItem *) items->data); + std::vector<SPItem*> items = selection->itemList(); + for(std::vector<SPItem*>::const_iterator i=items.begin();i!=items.end();i++){ + if (SP_IS_SHAPE(*i)) + return *i; } return NULL; } @@ -90,7 +87,7 @@ text_put_on_path() Inkscape::XML::Document *xml_doc = desktop->doc()->getReprDoc(); - if (!text || !shape || g_slist_length((GSList *) selection->itemList()) != 2) { + if (!text || !shape || selection->itemList().size() != 2) { desktop->getMessageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select <b>a text and a path</b> to put text on path.")); return; } @@ -199,11 +196,9 @@ text_remove_from_path() } bool did = false; - - for (GSList *items = g_slist_copy((GSList *) selection->itemList()); - items != NULL; - items = items->next) { - SPObject *obj = SP_OBJECT(items->data); + std::vector<SPItem*> items(selection->itemList()); + for(std::vector<SPItem*>::const_iterator i=items.begin();i!=items.end();i++){ + SPObject *obj = *i; if (SP_IS_TEXT_TEXTPATH(obj)) { SPObject *tp = obj->firstChild(); @@ -219,7 +214,7 @@ text_remove_from_path() } else { DocumentUndo::done(desktop->getDocument(), SP_VERB_CONTEXT_TEXT, _("Remove text from path")); - selection->setList(g_slist_copy((GSList *) selection->itemList())); // reselect to update statusbar description + selection->setList(selection->itemList()); // reselect to update statusbar description } } @@ -265,10 +260,9 @@ text_remove_all_kerns() bool did = false; - for (GSList *items = g_slist_copy((GSList *) selection->itemList()); - items != NULL; - items = items->next) { - SPObject *obj = SP_OBJECT(items->data); + std::vector<SPItem*> items = selection->itemList(); + for(std::vector<SPItem*>::const_iterator i=items.begin();i!=items.end();i++){ + SPObject *obj = *i; if (!SP_IS_TEXT(obj) && !SP_IS_TSPAN(obj) && !SP_IS_FLOWTEXT(obj)) { continue; @@ -302,7 +296,7 @@ text_flow_into_shape() SPItem *text = text_or_flowtext_in_selection(selection); SPItem *shape = shape_in_selection(selection); - if (!text || !shape || g_slist_length((GSList *) selection->itemList()) < 2) { + if (!text || !shape || selection->itemList().size() < 2) { desktop->getMessageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select <b>a text</b> and one or more <b>paths or shapes</b> to flow text into frame.")); return; } @@ -326,10 +320,9 @@ text_flow_into_shape() g_return_if_fail(SP_IS_FLOWREGION(object)); /* Add clones */ - for (GSList *items = (GSList *) selection->itemList(); - items != NULL; - items = items->next) { - SPItem *item = SP_ITEM(items->data); + std::vector<SPItem*> items = selection->itemList(); + for(std::vector<SPItem*>::const_iterator i=items.begin();i!=items.end();i++){ + SPItem *item = *i; if (SP_IS_SHAPE(item)){ Inkscape::XML::Node *clone = xml_doc->createElement("svg:use"); clone->setAttribute("x", "0"); @@ -394,23 +387,22 @@ text_unflow () Inkscape::Selection *selection = desktop->getSelection(); - if (!flowtext_in_selection(selection) || g_slist_length((GSList *) selection->itemList()) < 1) { + if (!flowtext_in_selection(selection) || selection->itemList().size() < 1) { desktop->getMessageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select <b>a flowed text</b> to unflow it.")); return; } - GSList *new_objs = NULL; + std::vector<SPItem*> new_objs; GSList *old_objs = NULL; - for (GSList *items = g_slist_copy((GSList *) selection->itemList()); - items != NULL; - items = items->next) { + std::vector<SPItem*> items = selection->itemList(); + for(std::vector<SPItem*>::const_iterator i=items.begin();i!=items.end();i++){ - if (!SP_IS_FLOWTEXT(SP_OBJECT(items->data))) { + if (!SP_IS_FLOWTEXT(*i)) { continue; } - SPItem *flowtext = SP_ITEM(items->data); + SPItem *flowtext = *i; // we discard transform when unflowing, but we must preserve expansion which is visible as // font size multiplier @@ -451,7 +443,7 @@ text_unflow () SPText *text = SP_TEXT(text_object); text->_adjustFontsizeRecursive(text, ex); - new_objs = g_slist_prepend (new_objs, text_object); + new_objs.push_back((SPItem*)text_object); old_objs = g_slist_prepend (old_objs, flowtext); Inkscape::GC::release(rtext); @@ -460,13 +452,13 @@ text_unflow () } selection->clear(); + reverse(new_objs.begin(),new_objs.end()); selection->setList(new_objs); for (GSList *i = old_objs; i; i = i->next) { SP_OBJECT(i->data)->deleteObject (true); } g_slist_free (old_objs); - g_slist_free (new_objs); DocumentUndo::done(doc, SP_VERB_CONTEXT_TEXT, _("Unflow flowed text")); @@ -487,11 +479,11 @@ flowtext_to_text() bool did = false; - GSList *reprs = NULL; - GSList *items = g_slist_copy((GSList *) selection->itemList()); - for (; items != NULL; items = items->next) { + std::vector<Inkscape::XML::Node*> reprs; + std::vector<SPItem*> items(selection->itemList()); + for(std::vector<SPItem*>::const_iterator i=items.begin();i!=items.end();i++){ - SPItem *item = (SPItem *) items->data; + SPItem *item = *i; if (!SP_IS_FLOWTEXT(item)) continue; @@ -519,10 +511,9 @@ flowtext_to_text() Inkscape::GC::release(repr); item->deleteObject(); - reprs = g_slist_prepend(reprs, repr); + reprs.push_back(repr); } - g_slist_free(items); if (did) { DocumentUndo::done(desktop->getDocument(), @@ -535,7 +526,6 @@ flowtext_to_text() _("<b>No flowed text(s)</b> to convert in the selection.")); } - g_slist_free(reprs); } diff --git a/src/text-editing.cpp b/src/text-editing.cpp index 4a962ab4c..050e223eb 100644 --- a/src/text-editing.cpp +++ b/src/text-editing.cpp @@ -67,12 +67,11 @@ static void te_update_layout_now (SPItem *item) void te_update_layout_now_recursive(SPItem *item) { if (SP_IS_GROUP(item)) { - GSList *item_list = sp_item_group_item_list(SP_GROUP(item)); - for(GSList* elem = item_list; elem; elem = elem->next) { - SPItem* list_item = static_cast<SPItem*>(elem->data); + std::vector<SPItem*> item_list = sp_item_group_item_list(SP_GROUP(item)); + for(std::vector<SPItem*>::const_iterator i=item_list.begin();i!=item_list.end();i++){ + SPItem* list_item = *i; te_update_layout_now_recursive(list_item); } - g_slist_free(item_list); } else if (SP_IS_TEXT(item)) SP_TEXT(item)->rebuildLayout(); else if (SP_IS_FLOWTEXT (item)) diff --git a/src/trace/CMakeLists.txt b/src/trace/CMakeLists.txt index 958907df6..bf7cfa276 100644 --- a/src/trace/CMakeLists.txt +++ b/src/trace/CMakeLists.txt @@ -28,6 +28,7 @@ set(trace_SRC potrace/auxiliary.h potrace/bitmap.h + potrace/bitops.h potrace/curve.h potrace/decompose.h potrace/greymap.h diff --git a/src/trace/potrace/inkscape-potrace.cpp b/src/trace/potrace/inkscape-potrace.cpp index e9e708f52..8f3bb7bdf 100644 --- a/src/trace/potrace/inkscape-potrace.cpp +++ b/src/trace/potrace/inkscape-potrace.cpp @@ -497,7 +497,7 @@ std::vector<TracingEngineResult> PotraceTracingEngine::traceBrightnessMulti(GdkP if ( !d.empty() ) { //### get style info int grayVal = (int)(256.0 * brightnessThreshold); - ustring style = ustring::compose("fill-opacity:1.0;fill:%1%2%3", twohex(grayVal), twohex(grayVal), twohex(grayVal) ); + ustring style = ustring::compose("fill-opacity:1.0;fill:#%1%2%3", twohex(grayVal), twohex(grayVal), twohex(grayVal) ); //g_message("### GOT '%s' \n", style.c_str()); TracingEngineResult result(style, d, nodeCount); diff --git a/src/trace/siox.cpp b/src/trace/siox.cpp index 065e891ed..9df4e561c 100644 --- a/src/trace/siox.cpp +++ b/src/trace/siox.cpp @@ -682,7 +682,7 @@ GdkPixbuf *SioxImage::getGdkPixbuf() } row += rowstride; } - + free(pixdata); return buf; } diff --git a/src/trace/trace.cpp b/src/trace/trace.cpp index cc3d000a3..91c230920 100644 --- a/src/trace/trace.cpp +++ b/src/trace/trace.cpp @@ -65,7 +65,7 @@ SPImage *Tracer::getSelectedSPImage() if (sioxEnabled) { SPImage *img = NULL; - GSList const *list = sel->itemList(); + std::vector<SPItem*> const list = sel->itemList(); std::vector<SPItem *> items; sioxShapes.clear(); @@ -74,13 +74,13 @@ SPImage *Tracer::getSelectedSPImage() them as bottom-to-top so that we can discover the image and any SPItems above it */ - for ( ; list ; list=list->next) + for (std::vector<SPItem*>::const_iterator i=list.begin() ; list.end()!=i ; i++) { - if (!SP_IS_ITEM(list->data)) + if (!SP_IS_ITEM(*i)) { continue; } - SPItem *item = SP_ITEM(list->data); + SPItem *item = *i; items.insert(items.begin(), item); } std::vector<SPItem *>::iterator iter; diff --git a/src/ui/CMakeLists.txt b/src/ui/CMakeLists.txt index 991d11feb..58af7d935 100644 --- a/src/ui/CMakeLists.txt +++ b/src/ui/CMakeLists.txt @@ -6,6 +6,7 @@ set(ui_SRC interface.cpp object-edit.cpp previewholder.cpp + selected-color.cpp shape-editor.cpp tool-factory.cpp tools-switch.cpp @@ -115,13 +116,20 @@ set(ui_SRC widget/anchor-selector.cpp widget/button.cpp widget/clipmaskicon.cpp + widget/color-entry.cpp + widget/color-icc-selector.cpp + widget/color-notebook.cpp widget/color-picker.cpp widget/color-preview.cpp + widget/color-scales.cpp + widget/color-slider.cpp + widget/color-wheel-selector.cpp widget/dock-item.cpp widget/dock.cpp widget/entity-entry.cpp widget/entry.cpp widget/filter-effect-chooser.cpp + widget/font-variants.cpp widget/frame.cpp widget/gimpcolorwheel.c widget/gimpspinscale.c @@ -174,6 +182,7 @@ set(ui_SRC previewable.h previewfillable.h previewholder.h + selected-color.h shape-editor.h tool-factory.h tools-switch.h @@ -217,6 +226,7 @@ set(ui_SRC dialog/livepatheffect-add.h dialog/livepatheffect-editor.h dialog/lpe-fillet-chamfer-properties.h + dialog/lpe-powerstroke-properties.h dialog/memory.h dialog/messages.h dialog/new-from-template.h @@ -233,6 +243,7 @@ set(ui_SRC dialog/svg-fonts-dialog.h dialog/swatches.h dialog/symbols.h + dialog/tags.h dialog/template-load-tab.h dialog/template-widget.h dialog/text-edit.h @@ -284,24 +295,36 @@ set(ui_SRC tools/tweak-tool.h tools/zoom-tool.h + widget/addtoicon.h widget/anchor-selector.h widget/attr-widget.h widget/button.h + widget/clipmaskicon.h + widget/color-entry.h + widget/color-icc-selector.h + widget/color-notebook.h widget/color-picker.h widget/color-preview.h + widget/color-scales.h + widget/color-slider.h + widget/color-wheel-selector.h widget/combo-enums.h widget/dock-item.h widget/dock.h widget/entity-entry.h widget/entry.h widget/filter-effect-chooser.h + widget/font-variants.h widget/frame.h widget/gimpspinscale.h widget/gimpcolorwheel.h + widget/highlight-picker.h + widget/insertordericon.h widget/imageicon.h widget/imagetoggler.h widget/labelled.h widget/layer-selector.h + widget/layertypeicon.h widget/licensor.h widget/notebook-page.h widget/object-composite-settings.h diff --git a/src/ui/Makefile_insert b/src/ui/Makefile_insert index f94cba4e9..bbfdb532c 100644 --- a/src/ui/Makefile_insert +++ b/src/ui/Makefile_insert @@ -19,6 +19,8 @@ ink_common_sources += \ ui/previewfillable.h \ ui/previewholder.cpp \ ui/previewholder.h \ + ui/selected-color.h \ + ui/selected-color.cpp \ ui/shape-editor.cpp \ ui/shape-editor.h \ ui/tool-factory.cpp \ diff --git a/src/ui/clipboard.cpp b/src/ui/clipboard.cpp index f1d0ff576..d6cf1f980 100644 --- a/src/ui/clipboard.cpp +++ b/src/ui/clipboard.cpp @@ -523,8 +523,9 @@ bool ClipboardManagerImpl::pasteSize(SPDesktop *desktop, bool separately, bool a // resize each object in the selection if (separately) { - for (GSList *i = const_cast<GSList*>(selection->itemList()) ; i ; i = i->next) { - SPItem *item = dynamic_cast<SPItem *>(static_cast<SPObject *>(i->data)); + std::vector<SPItem*> itemlist=selection->itemList(); + for(std::vector<SPItem*>::const_iterator i=itemlist.begin();i!=itemlist.end();i++){ + SPItem *item = *i; if (item) { Geom::OptRect obj_size = item->desktopVisualBounds(); if ( obj_size ) { @@ -578,8 +579,9 @@ bool ClipboardManagerImpl::pastePathEffect(SPDesktop *desktop) desktop->doc()->importDefs(tempdoc); // make sure all selected items are converted to paths first (i.e. rectangles) sp_selected_to_lpeitems(desktop); - for (GSList *itemptr = const_cast<GSList *>(selection->itemList()) ; itemptr ; itemptr = itemptr->next) { - SPItem *item = reinterpret_cast<SPItem*>(itemptr->data); + std::vector<SPItem*> itemlist=selection->itemList(); + for(std::vector<SPItem*>::const_iterator i=itemlist.begin();i!=itemlist.end();i++){ + SPItem *item = *i; _applyPathEffect(item, effectstack); } @@ -659,10 +661,10 @@ Glib::ustring ClipboardManagerImpl::getShapeOrTextObjectId(SPDesktop *desktop) */ void ClipboardManagerImpl::_copySelection(Inkscape::Selection *selection) { - GSList const *items = selection->itemList(); // copy the defs used by all items - for (GSList *i = const_cast<GSList *>(items) ; i != NULL ; i = i->next) { - SPItem *item = dynamic_cast<SPItem *>(static_cast<SPObject *>(i->data)); + std::vector<SPItem*> itemlist=selection->itemList(); + for(std::vector<SPItem*>::const_iterator i=itemlist.begin();i!=itemlist.end();i++){ + SPItem *item = *i; if (item) { _copyUsedDefs(item); } else { @@ -671,11 +673,11 @@ void ClipboardManagerImpl::_copySelection(Inkscape::Selection *selection) } // copy the representation of the items - GSList *sorted_items = g_slist_copy(const_cast<GSList *>(items)); - sorted_items = g_slist_sort(sorted_items, (GCompareFunc) sp_object_compare_position); + std::vector<SPItem*> sorted_items(itemlist); + sort(sorted_items.begin(),sorted_items.end(),sp_object_compare_position_bool); - for (GSList *i = sorted_items ; i ; i = i->next) { - SPItem *item = dynamic_cast<SPItem *>(static_cast<SPObject *>(i->data)); + for(std::vector<SPItem*>::const_iterator i=sorted_items.begin();i!=sorted_items.end();i++){ + SPItem *item = *i; if (item) { Inkscape::XML::Node *obj = item->getRepr(); Inkscape::XML::Node *obj_copy = _copyNode(obj, _doc, _root); @@ -703,8 +705,8 @@ void ClipboardManagerImpl::_copySelection(Inkscape::Selection *selection) } // copy style for Paste Style action - if (sorted_items) { - SPObject *object = static_cast<SPObject *>(sorted_items->data); + if (!sorted_items.empty()) { + SPObject *object = sorted_items[0]; SPItem *item = dynamic_cast<SPItem *>(object); if (item) { SPCSSAttr *style = take_style_from_item(item); @@ -727,7 +729,6 @@ void ClipboardManagerImpl::_copySelection(Inkscape::Selection *selection) sp_repr_set_point(_clipnode, "max", size->max()); } - g_slist_free(sorted_items); } @@ -1164,8 +1165,8 @@ void ClipboardManagerImpl::_onGet(Gtk::SelectionData &sel, guint /*info*/) sp_repr_get_double(nv, "inkscape:pageopacity", &opacity); bgcolor |= SP_COLOR_F_TO_U(opacity); } - - sp_export_png_file(_clipboardSPDoc, filename, area, width, height, dpi, dpi, bgcolor, NULL, NULL, true, NULL); + std::vector<SPItem*> x; + sp_export_png_file(_clipboardSPDoc, filename, area, width, height, dpi, dpi, bgcolor, NULL, NULL, true, x); } else { diff --git a/src/ui/dialog-events.cpp b/src/ui/dialog-events.cpp index 5bc8088a1..8856631c0 100644 --- a/src/ui/dialog-events.cpp +++ b/src/ui/dialog-events.cpp @@ -15,10 +15,6 @@ # include "config.h" #endif -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include <gtkmm/entry.h> #include <gtkmm/window.h> #include <gdk/gdkkeysyms.h> diff --git a/src/ui/dialog/aboutbox.h b/src/ui/dialog/aboutbox.h index 015e344a1..f3234b88d 100644 --- a/src/ui/dialog/aboutbox.h +++ b/src/ui/dialog/aboutbox.h @@ -15,14 +15,6 @@ #ifndef INKSCAPE_UI_DIALOG_ABOUTBOX_H #define INKSCAPE_UI_DIALOG_ABOUTBOX_H -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif - -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include <gtkmm/dialog.h> namespace Inkscape { diff --git a/src/ui/dialog/align-and-distribute.cpp b/src/ui/dialog/align-and-distribute.cpp index 65bc94011..882427912 100644 --- a/src/ui/dialog/align-and-distribute.cpp +++ b/src/ui/dialog/align-and-distribute.cpp @@ -97,9 +97,7 @@ void ActionAlign::do_action(SPDesktop *desktop, int index) Inkscape::Preferences *prefs = Inkscape::Preferences::get(); bool sel_as_group = prefs->getBool("/dialogs/align/sel-as-groups"); - using Inkscape::Util::GSListConstIterator; - std::list<SPItem *> selected; - selected.insert<GSListConstIterator<SPItem *> >(selected.end(), selection->itemList(), NULL); + std::vector<SPItem*> selected(selection->itemList()); if (selected.empty()) return; const Coeffs &a = _allCoeffs[index]; @@ -149,18 +147,19 @@ void ActionAlign::do_action(SPDesktop *desktop, int index) b = selection->preferredBounds(); //Move each item in the selected list separately - for (std::list<SPItem *>::iterator it(selected.begin()); + for (std::vector<SPItem*>::iterator it(selected.begin()); it != selected.end(); ++it) { + SPItem* item= *it; desktop->getDocument()->ensureUpToDate(); if (!sel_as_group) - b = (*it)->desktopPreferredBounds(); - if (b && (!focus || (*it) != focus)) { + b = (item)->desktopPreferredBounds(); + if (b && (!focus || (item) != focus)) { Geom::Point const sp(a.sx0 * b->min()[Geom::X] + a.sx1 * b->max()[Geom::X], a.sy0 * b->min()[Geom::Y] + a.sy1 * b->max()[Geom::Y]); Geom::Point const mp_rel( mp - sp ); if (LInfty(mp_rel) > 1e-9) { - sp_item_move_rel(*it, Geom::Translate(mp_rel)); + sp_item_move_rel(item, Geom::Translate(mp_rel)); changed = true; } } @@ -250,26 +249,24 @@ private : Inkscape::Selection *selection = desktop->getSelection(); if (!selection) return; - using Inkscape::Util::GSListConstIterator; - std::list<SPItem *> selected; - selected.insert<GSListConstIterator<SPItem *> >(selected.end(), selection->itemList(), NULL); + std::vector<SPItem*> selected(selection->itemList()); if (selected.empty()) return; //Check 2 or more selected objects - std::list<SPItem *>::iterator second(selected.begin()); + std::vector<SPItem*>::iterator second(selected.begin()); ++second; if (second == selected.end()) return; Inkscape::Preferences *prefs = Inkscape::Preferences::get(); int prefs_bbox = prefs->getBool("/tools/bounding_box"); std::vector< BBoxSort > sorted; - for (std::list<SPItem *>::iterator it(selected.begin()); + for (std::vector<SPItem*>::iterator it(selected.begin()); it != selected.end(); - ++it) - { - Geom::OptRect bbox = !prefs_bbox ? (*it)->desktopVisualBounds() : (*it)->desktopGeometricBounds(); + ++it){ + SPItem *item = *it; + Geom::OptRect bbox = !prefs_bbox ? (item)->desktopVisualBounds() : (item)->desktopGeometricBounds(); if (bbox) { - sorted.push_back(BBoxSort(*it, *bbox, _orientation, _kBegin, _kEnd)); + sorted.push_back(BBoxSort(item, *bbox, _orientation, _kBegin, _kEnd)); } } //sort bbox by anchors @@ -549,9 +546,7 @@ private : Inkscape::Selection *selection = desktop->getSelection(); if (!selection) return; - using Inkscape::Util::GSListConstIterator; - std::list<SPItem *> selected; - selected.insert<GSListConstIterator<SPItem *> >(selected.end(), selection->itemList(), NULL); + std::vector<SPItem*> selected(selection->itemList()); if (selected.empty()) return; //Check 2 or more selected objects @@ -569,15 +564,17 @@ private : } else { // sorting by ZOrder is outomatically done by not setting the center center.reset(); } - selected.sort(ActionExchangePositions::sort_compare); + sort(selected.begin(),selected.end(),sort_compare); } - std::list<SPItem *>::iterator it(selected.begin()); - Geom::Point p1 = (*it)->getCenter(); + std::vector<SPItem*>::iterator it(selected.begin()); + SPItem* item = *it; + Geom::Point p1 = item->getCenter(); for (++it ;it != selected.end(); ++it) { - Geom::Point p2 = (*it)->getCenter(); + item = *it; + Geom::Point p2 = item->getCenter(); Geom::Point delta = p1 - p2; - sp_item_move_rel((*it),Geom::Translate(delta[Geom::X],delta[Geom::Y] )); + sp_item_move_rel(item,Geom::Translate(delta[Geom::X],delta[Geom::Y] )); p1 = p2; } Geom::Point p2 = selected.front()->getCenter(); @@ -615,8 +612,8 @@ private : Inkscape::Preferences *prefs = Inkscape::Preferences::get(); int saved_compensation = prefs->getInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED); prefs->setInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED); - - unclump ((GSList *) _dialog.getDesktop()->getSelection()->itemList()); + std::vector<SPItem*> x(_dialog.getDesktop()->getSelection()->itemList()); + unclump (x); // restore compensation setting prefs->setInt("/options/clonecompensation/value", saved_compensation); @@ -646,9 +643,7 @@ private : Inkscape::Selection *selection = desktop->getSelection(); if (!selection) return; - using Inkscape::Util::GSListConstIterator; - std::list<SPItem *> selected; - selected.insert<GSListConstIterator<SPItem *> >(selected.end(), selection->itemList(), NULL); + std::vector<SPItem*> selected(selection->itemList()); if (selected.empty()) return; //Check 2 or more selected objects @@ -672,12 +667,13 @@ private : int saved_compensation = prefs->getInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED); prefs->setInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED); - for (std::list<SPItem *>::iterator it(selected.begin()); + for (std::vector<SPItem*>::iterator it(selected.begin()); it != selected.end(); ++it) { + SPItem* item = *it; desktop->getDocument()->ensureUpToDate(); - Geom::OptRect item_box = !prefs_bbox ? (*it)->desktopVisualBounds() : (*it)->desktopGeometricBounds(); + Geom::OptRect item_box = !prefs_bbox ? (item)->desktopVisualBounds() : (item)->desktopGeometricBounds(); if (item_box) { // find new center, staying within bbox double x = _dialog.randomize_bbox->min()[Geom::X] + (*item_box)[Geom::X].extent() /2 + @@ -686,7 +682,7 @@ private : g_random_double_range (0, (*_dialog.randomize_bbox)[Geom::Y].extent() - (*item_box)[Geom::Y].extent()); // displacement is the new center minus old: Geom::Point t = Geom::Point (x, y) - 0.5*(item_box->max() + item_box->min()); - sp_item_move_rel(*it, Geom::Translate(t)); + sp_item_move_rel(item, Geom::Translate(t)); } } @@ -745,9 +741,7 @@ private : Inkscape::Selection *selection = desktop->getSelection(); if (!selection) return; - using Inkscape::Util::GSListConstIterator; - std::list<SPItem *> selected; - selected.insert<GSListConstIterator<SPItem *> >(selected.end(), selection->itemList(), NULL); + std::vector<SPItem*> selected(selection->itemList()); if (selected.empty()) return; //Check 2 or more selected objects @@ -758,20 +752,21 @@ private : std::vector<Baselines> sorted; - for (std::list<SPItem *>::iterator it(selected.begin()); + for (std::vector<SPItem*>::iterator it(selected.begin()); it != selected.end(); ++it) { - if (SP_IS_TEXT (*it) || SP_IS_FLOWTEXT (*it)) { - Inkscape::Text::Layout const *layout = te_get_layout(*it); + SPItem* item = *it; + if (SP_IS_TEXT (item) || SP_IS_FLOWTEXT (item)) { + Inkscape::Text::Layout const *layout = te_get_layout(item); boost::optional<Geom::Point> pt = layout->baselineAnchorPoint(); if (pt) { - Geom::Point base = *pt * (*it)->i2dt_affine(); + Geom::Point base = *pt * (item)->i2dt_affine(); if (base[Geom::X] < b_min[Geom::X]) b_min[Geom::X] = base[Geom::X]; if (base[Geom::Y] < b_min[Geom::Y]) b_min[Geom::Y] = base[Geom::Y]; if (base[Geom::X] > b_max[Geom::X]) b_max[Geom::X] = base[Geom::X]; if (base[Geom::Y] > b_max[Geom::Y]) b_max[Geom::Y] = base[Geom::Y]; - Baselines b (*it, base, _orientation); + Baselines b (item, base, _orientation); sorted.push_back(b); } } @@ -801,18 +796,19 @@ private : } } else { - for (std::list<SPItem *>::iterator it(selected.begin()); + for (std::vector<SPItem*>::iterator it(selected.begin()); it != selected.end(); ++it) { - if (SP_IS_TEXT (*it) || SP_IS_FLOWTEXT (*it)) { - Inkscape::Text::Layout const *layout = te_get_layout(*it); + SPItem* item = *it; + if (SP_IS_TEXT (item) || SP_IS_FLOWTEXT (item)) { + Inkscape::Text::Layout const *layout = te_get_layout(item); boost::optional<Geom::Point> pt = layout->baselineAnchorPoint(); if (pt) { - Geom::Point base = *pt * (*it)->i2dt_affine(); + Geom::Point base = *pt * (item)->i2dt_affine(); Geom::Point t(0.0, 0.0); t[_orientation] = b_min[_orientation] - base[_orientation]; - sp_item_move_rel(*it, Geom::Translate(t)); + sp_item_move_rel(item, Geom::Translate(t)); changed = true; } } diff --git a/src/ui/dialog/calligraphic-profile-rename.h b/src/ui/dialog/calligraphic-profile-rename.h index fa13db196..4ef71900b 100644 --- a/src/ui/dialog/calligraphic-profile-rename.h +++ b/src/ui/dialog/calligraphic-profile-rename.h @@ -15,10 +15,6 @@ # include "config.h" #endif -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -# include <glibmm/threads.h> -#endif - #include <gtkmm/dialog.h> #include <gtkmm/entry.h> #include <gtkmm/label.h> diff --git a/src/ui/dialog/clonetiler.cpp b/src/ui/dialog/clonetiler.cpp index bd3ad0254..f84a2ffd6 100644 --- a/src/ui/dialog/clonetiler.cpp +++ b/src/ui/dialog/clonetiler.cpp @@ -1359,7 +1359,7 @@ void CloneTiler::clonetiler_change_selection(Inkscape::Selection *selection, Gtk return; } - if (g_slist_length ((GSList *) selection->itemList()) > 1) { + if (selection->itemList().size() > 1) { gtk_widget_set_sensitive (buttons, FALSE); gtk_label_set_markup (GTK_LABEL(status), _("<small>More than one object selected.</small>")); return; @@ -2096,7 +2096,7 @@ void CloneTiler::clonetiler_unclump(GtkWidget */*widget*/, void *) Inkscape::Selection *selection = desktop->getSelection(); // check if something is selected - if (selection->isEmpty() || g_slist_length((GSList *) selection->itemList()) > 1) { + if (selection->isEmpty() || selection->itemList().size() > 1) { desktop->getMessageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select <b>one object</b> whose tiled clones to unclump.")); return; } @@ -2104,20 +2104,18 @@ void CloneTiler::clonetiler_unclump(GtkWidget */*widget*/, void *) SPObject *obj = selection->singleItem(); SPObject *parent = obj->parent; - GSList *to_unclump = NULL; // not including the original + std::vector<SPItem*> to_unclump; // not including the original for (SPObject *child = parent->firstChild(); child != NULL; child = child->next) { if (clonetiler_is_a_clone_of (child, obj)) { - to_unclump = g_slist_prepend (to_unclump, child); + to_unclump.push_back((SPItem*)child); } } desktop->getDocument()->ensureUpToDate(); - + reverse(to_unclump.begin(),to_unclump.end()); unclump (to_unclump); - g_slist_free (to_unclump); - DocumentUndo::done(desktop->getDocument(), SP_VERB_DIALOG_CLONETILER, _("Unclump tiled clones")); } @@ -2147,7 +2145,7 @@ void CloneTiler::clonetiler_remove(GtkWidget */*widget*/, GtkWidget *dlg, bool d Inkscape::Selection *selection = desktop->getSelection(); // check if something is selected - if (selection->isEmpty() || g_slist_length((GSList *) selection->itemList()) > 1) { + if (selection->isEmpty() || selection->itemList().size() > 1) { desktop->getMessageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select <b>one object</b> whose tiled clones to remove.")); return; } @@ -2225,7 +2223,7 @@ void CloneTiler::clonetiler_apply(GtkWidget */*widget*/, GtkWidget *dlg) } // Check if more than one object is selected. - if (g_slist_length((GSList *) selection->itemList()) > 1) { + if (selection->itemList().size() > 1) { desktop->getMessageStack()->flash(Inkscape::ERROR_MESSAGE, _("If you want to clone several objects, <b>group</b> them and <b>clone the group</b>.")); return; } diff --git a/src/ui/dialog/color-item.cpp b/src/ui/dialog/color-item.cpp index a49a47d5c..6603d5c69 100644 --- a/src/ui/dialog/color-item.cpp +++ b/src/ui/dialog/color-item.cpp @@ -17,10 +17,6 @@ #include <errno.h> -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include <gtkmm/label.h> #include <glibmm/i18n.h> #include <cairo.h> diff --git a/src/ui/dialog/debug.cpp b/src/ui/dialog/debug.cpp index 9e2287f80..d127261c0 100644 --- a/src/ui/dialog/debug.cpp +++ b/src/ui/dialog/debug.cpp @@ -13,10 +13,6 @@ # include <config.h> #endif -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include <gtkmm/box.h> #include <gtkmm/dialog.h> #include <gtkmm/textview.h> diff --git a/src/ui/dialog/document-properties.cpp b/src/ui/dialog/document-properties.cpp index c381ed755..b04e8ecc1 100644 --- a/src/ui/dialog/document-properties.cpp +++ b/src/ui/dialog/document-properties.cpp @@ -1672,10 +1672,10 @@ void DocumentProperties::onDocUnitChange() Inkscape::XML::Node *repr = getDesktop()->getNamedView()->getRepr(); - Inkscape::Util::Unit const *old_doc_unit = unit_table.getUnit("px"); + /*Inkscape::Util::Unit const *old_doc_unit = unit_table.getUnit("px"); if(repr->attribute("inkscape:document-units")) { old_doc_unit = unit_table.getUnit(repr->attribute("inkscape:document-units")); - } + }*/ Inkscape::Util::Unit const *doc_unit = _rum_deflt.getUnit(); // Set document unit diff --git a/src/ui/dialog/export.cpp b/src/ui/dialog/export.cpp index 6d90c792e..1edfdfe80 100644 --- a/src/ui/dialog/export.cpp +++ b/src/ui/dialog/export.cpp @@ -20,10 +20,6 @@ // This has to be included prior to anything that includes setjmp.h, it croaks otherwise #include <png.h> -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include <gtkmm/box.h> #include <gtkmm/buttonbox.h> #include <gtkmm/dialog.h> @@ -605,7 +601,7 @@ void Export::onBatchClicked () void Export::updateCheckbuttons () { - gint num = g_slist_length((GSList *) SP_ACTIVE_DESKTOP->getSelection()->itemList()); + gint num = SP_ACTIVE_DESKTOP->getSelection()->itemList().size(); if (num >= 2) { batch_export.set_sensitive(true); batch_export.set_label(g_strdup_printf (ngettext("B_atch export %d selected object","B_atch export %d selected objects",num), num)); @@ -817,9 +813,9 @@ void Export::onAreaToggled () one that's nice */ if (filename.empty()) { const gchar * id = "object"; - const GSList * reprlst = SP_ACTIVE_DESKTOP->getSelection()->reprList(); - for(; reprlst != NULL; reprlst = reprlst->next) { - Inkscape::XML::Node * repr = (Inkscape::XML::Node *)reprlst->data; + const std::vector<XML::Node*> reprlst = SP_ACTIVE_DESKTOP->getSelection()->reprList(); + for(std::vector<XML::Node*>::const_iterator i=reprlst.begin(); reprlst.end() != i; i++) { + Inkscape::XML::Node * repr = *i; if (repr->attribute("id")) { id = repr->attribute("id"); break; @@ -949,17 +945,19 @@ Gtk::Dialog * Export::create_progress_dialog (Glib::ustring progress_text) { Glib::ustring Export::filename_add_extension (Glib::ustring filename, Glib::ustring extension) { Glib::ustring::size_type dot; + Glib::ustring::size_type dot_ext; dot = filename.find_last_of("."); - if ( !dot ) + dot_ext = filename.lowercase().rfind("." + extension.lowercase()); + if ( dot == std::string::npos ) { return filename = filename + "." + extension; } else { - if (dot==filename.find_last_of(Glib::ustring::compose(".", extension))) + if (dot == dot_ext) { - return filename; + return filename = filename; } else { @@ -1010,7 +1008,7 @@ void Export::onExport () if (batch_export.get_active ()) { // Batch export of selected objects - gint num = g_slist_length(const_cast<GSList *>(desktop->getSelection()->itemList())); + gint num = (desktop->getSelection()->itemList()).size(); gint n = 0; if (num < 1) { @@ -1024,8 +1022,9 @@ void Export::onExport () gint export_count = 0; - for (GSList *i = const_cast<GSList *>(desktop->getSelection()->itemList()); i && !interrupted; i = i->next) { - SPItem *item = reinterpret_cast<SPItem *>(i->data); + std::vector<SPItem*> itemlist=desktop->getSelection()->itemList(); + for(std::vector<SPItem*>::const_iterator i = itemlist.begin();i!=itemlist.end() && !interrupted ;i++){ + SPItem *item = *i; prog_dlg->set_data("current", GINT_TO_POINTER(n)); prog_dlg->set_data("total", GINT_TO_POINTER(num)); @@ -1063,13 +1062,13 @@ void Export::onExport () _("Exporting file <b>%s</b>..."), safeFile), desktop); MessageCleaner msgFlashCleanup(desktop->messageStack()->flashF(Inkscape::IMMEDIATE_MESSAGE, _("Exporting file <b>%s</b>..."), safeFile), desktop); - + std::vector<SPItem*> x; if (!sp_export_png_file (doc, path.c_str(), *area, width, height, dpi, dpi, nv->pagecolor, onProgressCallback, (void*)prog_dlg, TRUE, // overwrite without asking - hide ? const_cast<GSList *>(desktop->getSelection()->itemList()) : NULL + hide ? (desktop->getSelection()->itemList()) : x )) { gchar * error = g_strdup_printf(_("Could not export to filename %s.\n"), safeFile); @@ -1153,12 +1152,13 @@ void Export::onExport () prog_dlg->set_data("total", GINT_TO_POINTER(0)); /* Do export */ + std::vector<SPItem*> x; ExportResult status = sp_export_png_file(desktop->getDocument(), path.c_str(), Geom::Rect(Geom::Point(x0, y0), Geom::Point(x1, y1)), width, height, xdpi, ydpi, nv->pagecolor, onProgressCallback, (void*)prog_dlg, FALSE, - hide ? const_cast<GSList *>(desktop->getSelection()->itemList()) : NULL + hide ? (desktop->getSelection()->itemList()) : x ); if (status == EXPORT_ERROR) { gchar * safeFile = Inkscape::IO::sanitizeString(path.c_str()); @@ -1224,7 +1224,7 @@ void Export::onExport () break; } case SELECTION_SELECTION: { - const GSList * reprlst; + std::vector<XML::Node*> reprlst; SPDocument * doc = SP_ACTIVE_DOCUMENT; bool modified = false; @@ -1232,8 +1232,8 @@ void Export::onExport () DocumentUndo::setUndoSensitive(doc, false); reprlst = desktop->getSelection()->reprList(); - for(; reprlst != NULL; reprlst = reprlst->next) { - Inkscape::XML::Node * repr = static_cast<Inkscape::XML::Node *>(reprlst->data); + for(std::vector<Inkscape::XML::Node*>::const_iterator i=reprlst.begin(); reprlst.end() != i; i++) { + Inkscape::XML::Node * repr = *i; const gchar * temp_string; Glib::ustring dir = Glib::path_get_dirname(filename.c_str()); const gchar* docURI=SP_ACTIVE_DOCUMENT->getURI(); diff --git a/src/ui/dialog/filedialogimpl-gtkmm.h b/src/ui/dialog/filedialogimpl-gtkmm.h index 6687915d7..7501b5e14 100644 --- a/src/ui/dialog/filedialogimpl-gtkmm.h +++ b/src/ui/dialog/filedialogimpl-gtkmm.h @@ -17,14 +17,6 @@ #ifndef __FILE_DIALOGIMPL_H__ #define __FILE_DIALOGIMPL_H__ -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif - -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - //Gtk includes #include <gtkmm/filechooserdialog.h> #include <glib/gstdio.h> diff --git a/src/ui/dialog/filedialogimpl-win32.h b/src/ui/dialog/filedialogimpl-win32.h index 8c3b6bad6..b254c3ef6 100644 --- a/src/ui/dialog/filedialogimpl-win32.h +++ b/src/ui/dialog/filedialogimpl-win32.h @@ -16,11 +16,6 @@ #include <glibmm.h> #ifdef WIN32 -#if WITH_GLIBMM_2_32 -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -# include <glibmm/threads.h> -#endif -#endif #include "filedialogimpl-gtkmm.h" diff --git a/src/ui/dialog/filter-effects-dialog.cpp b/src/ui/dialog/filter-effects-dialog.cpp index 077c6f5ca..1ff9e4a1b 100644 --- a/src/ui/dialog/filter-effects-dialog.cpp +++ b/src/ui/dialog/filter-effects-dialog.cpp @@ -8,6 +8,7 @@ * Felipe C. da S. Sanches <juca@members.fsf.org> * Jon A. Cruz <jon@joncruz.org> * Abhishek Sharma + * insaner * * Copyright (C) 2007 Authors * @@ -690,7 +691,7 @@ private: void select_svg_element(){ Inkscape::Selection* sel = _desktop->getSelection(); if (sel->isEmpty()) return; - Inkscape::XML::Node* node = (Inkscape::XML::Node*) g_slist_nth_data((GSList *)sel->reprList(), 0); + Inkscape::XML::Node* node = sel->reprList()[0]; if (!node || !node->matchAttributeName("id")) return; std::ostringstream xlikhref; @@ -1360,8 +1361,15 @@ FilterEffectsDialog::FilterModifier::FilterModifier(FilterEffectsDialog& d) ((Gtk::CellRendererText*)_list.get_column(1)->get_first_cell())-> signal_edited().connect(sigc::mem_fun(*this, &FilterEffectsDialog::FilterModifier::on_name_edited)); + _list.append_column("#", _columns.count); + _list.get_column(2)->set_sizing(Gtk::TREE_VIEW_COLUMN_AUTOSIZE); + _list.get_column(2)->set_expand(false); + sw->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); _list.get_column(1)->set_resizable(true); + _list.get_column(1)->set_sizing(Gtk::TREE_VIEW_COLUMN_AUTOSIZE); + _list.get_column(1)->set_expand(true); + _list.set_reorderable(true); _list.enable_model_drag_dest (Gdk::ACTION_MOVE); @@ -1465,9 +1473,9 @@ void FilterEffectsDialog::FilterModifier::update_selection(Selection *sel) } std::set<SPObject*> used; - - for (GSList const *i = sel->itemList(); i != NULL; i = i->next) { - SPObject *obj = SP_OBJECT (i->data); + std::vector<SPItem*> itemlist=sel->itemList(); + for(std::vector<SPItem*>::const_iterator i=itemlist.begin(); itemlist.end() != i; i++) { + SPObject *obj = *i; SPStyle *style = obj->style; if (!style || !SP_IS_ITEM(obj)) { continue; @@ -1494,6 +1502,7 @@ void FilterEffectsDialog::FilterModifier::update_selection(Selection *sel) (*iter)[_columns.sel] = 0; } } + update_counts(); } void FilterEffectsDialog::FilterModifier::on_filter_selection_changed() @@ -1545,10 +1554,9 @@ void FilterEffectsDialog::FilterModifier::on_selection_toggled(const Glib::ustri if((*iter)[_columns.sel] == 1) filter = 0; - GSList const *items = sel->itemList(); - - for (GSList const *i = items; i != NULL; i = i->next) { - SPItem * item = SP_ITEM(i->data); + std::vector<SPItem*> itemlist=sel->itemList(); + for(std::vector<SPItem*>::const_iterator i=itemlist.begin(); itemlist.end() != i; i++) { + SPItem * item = *i; SPStyle *style = item->style; g_assert(style != NULL); @@ -1566,6 +1574,15 @@ void FilterEffectsDialog::FilterModifier::on_selection_toggled(const Glib::ustri } } + +void FilterEffectsDialog::FilterModifier::update_counts() +{ + for(Gtk::TreeModel::iterator i = _model->children().begin(); i != _model->children().end(); ++i) { + SPFilter* f = SP_FILTER((*i)[_columns.filter]); + (*i)[_columns.count] = f->getRefCount(); + } +} + /* Add all filters in the document to the combobox. Keeps the same selection if possible, otherwise selects the first element */ void FilterEffectsDialog::FilterModifier::update_filters() @@ -1650,12 +1667,13 @@ void FilterEffectsDialog::FilterModifier::remove_filter() SPDocument* doc = filter->document; // Delete all references to this filter - GSList *all = get_all_items(NULL, _desktop->currentRoot(), _desktop, false, false, true, NULL); - for (GSList *i = all; i != NULL; i = i->next) { - if (!SP_IS_ITEM(i->data)) { + std::vector<SPItem*> x,y; + std::vector<SPItem*> all = get_all_items(x, _desktop->currentRoot(), _desktop, false, false, true, y); + for(std::vector<SPItem*>::const_iterator i=all.begin(); all.end() != i; i++) { + if (!SP_IS_ITEM(*i)) { continue; } - SPItem *item = SP_ITEM(i->data); + SPItem *item = *i; if (!item->style) { continue; } @@ -1668,9 +1686,6 @@ void FilterEffectsDialog::FilterModifier::remove_filter() } } } - if (all) { - g_slist_free(all); - } //XML Tree being used directly here while it shouldn't be. sp_repr_unparent(filter->getRepr()); @@ -2422,7 +2437,7 @@ bool FilterEffectsDialog::PrimitiveList::on_motion_notify_event(GdkEventMotion* get_visible_rect(vis); int vis_x, vis_y; - int vis_x2, vis_y2; // NOTE: insaner added -- necessary to get the scrolling while dragging to work + int vis_x2, vis_y2; convert_widget_to_tree_coords(vis.get_x(), vis.get_y(), vis_x2, vis_y2); convert_tree_to_widget_coords(vis.get_x(), vis.get_y(), vis_x, vis_y); @@ -2442,7 +2457,6 @@ bool FilterEffectsDialog::PrimitiveList::on_motion_notify_event(GdkEventMotion* else _autoscroll_y = 0; - // NOTE: insaner added -- necessary to get the scrolling while dragging to work double e2 = ( e->x - vis_x2/2); // horizontal scrolling if(e2 < vis_x) @@ -2755,20 +2769,22 @@ FilterEffectsDialog::FilterEffectsDialog() Gtk::ScrolledWindow* sw_infobox = Gtk::manage(new Gtk::ScrolledWindow); Gtk::HBox* infobox = Gtk::manage(new Gtk::HBox(/*homogeneous:*/false, /*spacing:*/4)); Gtk::HBox* hb_prims = Gtk::manage(new Gtk::HBox); + Gtk::VBox* vb_prims = Gtk::manage(new Gtk::VBox); _getContents()->add(*hpaned); hpaned->pack1(_filter_modifier); hpaned->pack2(_primitive_box); _primitive_box.pack_start(*sw_prims); - _primitive_box.pack_start(*hb_prims, false, false); _primitive_box.pack_start(*sw_infobox, false, false); sw_prims->add(_primitive_list); - sw_infobox->add(*infobox); + sw_infobox->add(*vb_prims); infobox->pack_start(_infobox_icon, false, false); infobox->pack_start(_infobox_desc, false, false); _infobox_desc.set_line_wrap(true); - _infobox_desc.set_size_request(200, -1); + _infobox_desc.set_size_request(250, -1); + vb_prims->pack_start(*hb_prims); + vb_prims->pack_start(*infobox); hb_prims->pack_start(_add_primitive, false, false); hb_prims->pack_start(_add_primitive_type, false, false); @@ -2784,7 +2800,7 @@ FilterEffectsDialog::FilterEffectsDialog() _add_primitive_type.signal_changed().connect( sigc::mem_fun(*this, &FilterEffectsDialog::update_primitive_infobox)); - sw_prims->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); /* NOTE: insaner -- SCROLL the connections panel thing!!! */ + sw_prims->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); sw_prims->set_shadow_type(Gtk::SHADOW_IN); sw_infobox->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_NEVER); diff --git a/src/ui/dialog/filter-effects-dialog.h b/src/ui/dialog/filter-effects-dialog.h index 3fc19e7de..a067cd70c 100644 --- a/src/ui/dialog/filter-effects-dialog.h +++ b/src/ui/dialog/filter-effects-dialog.h @@ -4,6 +4,7 @@ /* Authors: * Nicholas Bishop <nicholasbishop@gmail.com> * Rodrigo Kumpera <kumpera@gmail.com> + * insaner * * Copyright (C) 2007 Authors * @@ -69,11 +70,13 @@ private: add(filter); add(label); add(sel); + add(count); } Gtk::TreeModelColumn<SPFilter*> filter; Gtk::TreeModelColumn<Glib::ustring> label; Gtk::TreeModelColumn<int> sel; + Gtk::TreeModelColumn<int> count; }; void setTargetDesktop(SPDesktop *desktop); @@ -89,6 +92,7 @@ private: bool on_filter_move(const Glib::RefPtr<Gdk::DragContext>& /*context*/, int x, int y, guint /*time*/); void on_selection_toggled(const Glib::ustring&); + void update_counts(); void update_filters(); void filter_list_button_release(GdkEventButton*); void add_filter(); diff --git a/src/ui/dialog/find.cpp b/src/ui/dialog/find.cpp index 951cb01ea..a8ac42a1b 100644 --- a/src/ui/dialog/find.cpp +++ b/src/ui/dialog/find.cpp @@ -549,7 +549,7 @@ bool Find::item_font_match(SPItem *item, const gchar *text, bool exact, bool cas } -GSList *Find::filter_fields (GSList *l, bool exact, bool casematch) +std::vector<SPItem*> Find::filter_fields (std::vector<SPItem*> &l, bool exact, bool casematch) { Glib::ustring tmp = entry_find.getEntry()->get_text(); if (tmp.empty()) { @@ -557,17 +557,17 @@ GSList *Find::filter_fields (GSList *l, bool exact, bool casematch) } gchar* text = g_strdup(tmp.c_str()); - GSList *in = l; - GSList *out = NULL; + std::vector<SPItem*> in = l; + std::vector<SPItem*> out; if (check_searchin_text.get_active()) { - for (GSList *i = in; i != NULL; i = i->next) { - SPObject *obj = reinterpret_cast<SPObject *>(i->data); + for(std::vector<SPItem*>::const_reverse_iterator i=in.rbegin(); in.rend() != i; i++) { + SPObject *obj = *i; SPItem *item = dynamic_cast<SPItem *>(obj); g_assert(item != NULL); if (item_text_match(item, text, exact, casematch)) { - if (!g_slist_find(out, i->data)) { - out = g_slist_prepend (out, i->data); + if (out.end()==find(out.begin(),out.end(), *i)) { + out.push_back(*i); if (_action_replace) { item_text_match(item, text, exact, casematch, _action_replace); } @@ -584,12 +584,12 @@ GSList *Find::filter_fields (GSList *l, bool exact, bool casematch) bool attrvalue = check_attributevalue.get_active(); if (ids) { - for (GSList *i = in; i != NULL; i = i->next) { - SPObject *obj = reinterpret_cast<SPObject *>(i->data); + for(std::vector<SPItem*>::const_reverse_iterator i=in.rbegin(); in.rend() != i; i++) { + SPObject *obj = *i; SPItem *item = dynamic_cast<SPItem *>(obj); if (item_id_match(item, text, exact, casematch)) { - if (!g_slist_find(out, i->data)) { - out = g_slist_prepend (out, i->data); + if (out.end()==find(out.begin(),out.end(), *i)) { + out.push_back(*i); if (_action_replace) { item_id_match(item, text, exact, casematch, _action_replace); } @@ -600,14 +600,13 @@ GSList *Find::filter_fields (GSList *l, bool exact, bool casematch) if (style) { - for (GSList *i = in; i != NULL; i = i->next) { - SPObject *obj = reinterpret_cast<SPObject *>(i->data); + for(std::vector<SPItem*>::const_reverse_iterator i=in.rbegin(); in.rend() != i; i++) { + SPObject *obj = *i; SPItem *item = dynamic_cast<SPItem *>(obj); g_assert(item != NULL); if (item_style_match(item, text, exact, casematch)) { - if (!g_slist_find(out, i->data)) - if (!g_slist_find(out, i->data)) { - out = g_slist_prepend (out, i->data); + if (out.end()==find(out.begin(),out.end(), *i)){ + out.push_back(*i); if (_action_replace) { item_style_match(item, text, exact, casematch, _action_replace); } @@ -618,13 +617,13 @@ GSList *Find::filter_fields (GSList *l, bool exact, bool casematch) if (attrname) { - for (GSList *i = in; i != NULL; i = i->next) { - SPObject *obj = reinterpret_cast<SPObject *>(i->data); + for(std::vector<SPItem*>::const_reverse_iterator i=in.rbegin(); in.rend() != i; i++) { + SPObject *obj = *i; SPItem *item = dynamic_cast<SPItem *>(obj); g_assert(item != NULL); if (item_attr_match(item, text, exact, casematch)) { - if (!g_slist_find(out, i->data)) { - out = g_slist_prepend (out, i->data); + if (out.end()==find(out.begin(),out.end(), *i)) { + out.push_back(*i); if (_action_replace) { item_attr_match(item, text, exact, casematch, _action_replace); } @@ -635,13 +634,13 @@ GSList *Find::filter_fields (GSList *l, bool exact, bool casematch) if (attrvalue) { - for (GSList *i = in; i != NULL; i = i->next) { - SPObject *obj = reinterpret_cast<SPObject *>(i->data); + for(std::vector<SPItem*>::const_reverse_iterator i=in.rbegin(); in.rend() != i; i++) { + SPObject *obj = *i; SPItem *item = dynamic_cast<SPItem *>(obj); g_assert(item != NULL); if (item_attrvalue_match(item, text, exact, casematch)) { - if (!g_slist_find(out, i->data)) { - out = g_slist_prepend (out, i->data); + if (out.end()==find(out.begin(),out.end(), *i)) { + out.push_back(*i); if (_action_replace) { item_attrvalue_match(item, text, exact, casematch, _action_replace); } @@ -652,13 +651,13 @@ GSList *Find::filter_fields (GSList *l, bool exact, bool casematch) if (font) { - for (GSList *i = in; i != NULL; i = i->next) { - SPObject *obj = reinterpret_cast<SPObject *>(i->data); + for(std::vector<SPItem*>::const_reverse_iterator i=in.rbegin(); in.rend() != i; i++) { + SPObject *obj = *i; SPItem *item = dynamic_cast<SPItem *>(obj); g_assert(item != NULL); if (item_font_match(item, text, exact, casematch)) { - if (!g_slist_find(out, i->data)) { - out = g_slist_prepend (out, i->data); + if (out.end()==find(out.begin(),out.end(),*i)) { + out.push_back(*i); if (_action_replace) { item_font_match(item, text, exact, casematch, _action_replace); } @@ -716,29 +715,29 @@ bool Find::item_type_match (SPItem *item) return false; } -GSList *Find::filter_types (GSList *l) +std::vector<SPItem*> Find::filter_types (std::vector<SPItem*> &l) { - GSList *n = NULL; - for (GSList *i = l; i != NULL; i = i->next) { - SPObject *obj = reinterpret_cast<SPObject *>(i->data); + std::vector<SPItem*> n; + for(std::vector<SPItem*>::const_reverse_iterator i=l.rbegin(); l.rend() != i; i++) { + SPObject *obj = *i; SPItem *item = dynamic_cast<SPItem *>(obj); g_assert(item != NULL); if (item_type_match(item)) { - n = g_slist_prepend (n, i->data); + n.push_back(*i); } } return n; } -GSList *Find::filter_list (GSList *l, bool exact, bool casematch) +std::vector<SPItem*> &Find::filter_list (std::vector<SPItem*> &l, bool exact, bool casematch) { l = filter_types (l); l = filter_fields (l, exact, casematch); return l; } -GSList *Find::all_items (SPObject *r, GSList *l, bool hidden, bool locked) +std::vector<SPItem*> &Find::all_items (SPObject *r, std::vector<SPItem*> &l, bool hidden, bool locked) { if (dynamic_cast<SPDefs *>(r)) { return l; // we're not interested in items in defs @@ -752,7 +751,7 @@ GSList *Find::all_items (SPObject *r, GSList *l, bool hidden, bool locked) SPItem *item = dynamic_cast<SPItem *>(child); if (item && !child->cloned && !desktop->isLayer(item)) { if ((hidden || !desktop->itemIsHidden(item)) && (locked || !item->isLocked())) { - l = g_slist_prepend (l, child); + l.insert(l.begin(),(SPItem*)child); } } l = all_items (child, l, hidden, locked); @@ -760,16 +759,17 @@ GSList *Find::all_items (SPObject *r, GSList *l, bool hidden, bool locked) return l; } -GSList *Find::all_selection_items (Inkscape::Selection *s, GSList *l, SPObject *ancestor, bool hidden, bool locked) +std::vector<SPItem*> &Find::all_selection_items (Inkscape::Selection *s, std::vector<SPItem*> &l, SPObject *ancestor, bool hidden, bool locked) { - for (GSList *i = (GSList *) s->itemList(); i != NULL; i = i->next) { - SPObject *obj = reinterpret_cast<SPObject *>(i->data); + std::vector<SPItem*> itemlist=s->itemList(); + for(std::vector<SPItem*>::const_reverse_iterator i=itemlist.rbegin(); itemlist.rend() != i; i++) { + SPObject *obj = *i; SPItem *item = dynamic_cast<SPItem *>(obj); g_assert(item != NULL); if (item && !item->cloned && !desktop->isLayer(item)) { if (!ancestor || ancestor->isAncestorOf(item)) { if ((hidden || !desktop->itemIsHidden(item)) && (locked || !item->isLocked())) { - l = g_slist_prepend (l, i->data); + l.push_back(*i); } } } @@ -817,7 +817,7 @@ void Find::onAction() bool casematch = check_case_sensitive.get_active(); blocked = true; - GSList *l = NULL; + std::vector<SPItem*> l; if (check_scope_selection.get_active()) { if (check_scope_layer.get_active()) { l = all_selection_items (desktop->selection, l, desktop->currentLayer(), hidden, locked); @@ -831,12 +831,12 @@ void Find::onAction() l = all_items(desktop->getDocument()->getRoot(), l, hidden, locked); } } - guint all = g_slist_length (l); + guint all = l.size(); - GSList *n = filter_list (l, exact, casematch); + std::vector<SPItem*> n = filter_list (l, exact, casematch); - if (n != NULL) { - int count = g_slist_length (n); + if (!n.empty()) { + int count = n.size(); desktop->messageStack()->flashF(Inkscape::NORMAL_MESSAGE, // TRANSLATORS: "%s" is replaced with "exact" or "partial" when this string is displayed ngettext("<b>%d</b> object found (out of <b>%d</b>), %s match.", @@ -857,7 +857,7 @@ void Find::onAction() Inkscape::Selection *selection = desktop->getSelection(); selection->clear(); selection->setList(n); - SPObject *obj = reinterpret_cast<SPObject *>(n->data); + SPObject *obj = n[0]; SPItem *item = dynamic_cast<SPItem *>(obj); g_assert(item != NULL); scroll_to_show_item(desktop, item); diff --git a/src/ui/dialog/find.h b/src/ui/dialog/find.h index c0c635f94..4bcb900b6 100644 --- a/src/ui/dialog/find.h +++ b/src/ui/dialog/find.h @@ -148,10 +148,10 @@ protected: /** * Function to filter a list of items based on the item type by calling each item_XXX_match function */ - GSList * filter_fields (GSList *l, bool exact, bool casematch); + std::vector<SPItem*> filter_fields (std::vector<SPItem*> &l, bool exact, bool casematch); bool item_type_match (SPItem *item); - GSList * filter_types (GSList *l); - GSList * filter_list (GSList *l, bool exact, bool casematch); + std::vector<SPItem*> filter_types (std::vector<SPItem*> &l); + std::vector<SPItem*> & filter_list (std::vector<SPItem*> &l, bool exact, bool casematch); /** * Find a string within a string and returns true if found with options for exact and casematching @@ -172,12 +172,12 @@ protected: * recursive function to return a list of all the items in the SPObject tree * */ - GSList * all_items (SPObject *r, GSList *l, bool hidden, bool locked); + std::vector<SPItem*> & all_items (SPObject *r, std::vector<SPItem*> &l, bool hidden, bool locked); /** * to return a list of all the selected items * */ - GSList * all_selection_items (Inkscape::Selection *s, GSList *l, SPObject *ancestor, bool hidden, bool locked); + std::vector<SPItem*> & all_selection_items (Inkscape::Selection *s, std::vector<SPItem*> &l, SPObject *ancestor, bool hidden, bool locked); /** * Shrink the dialog size when the expander widget is closed diff --git a/src/ui/dialog/floating-behavior.cpp b/src/ui/dialog/floating-behavior.cpp index 11db14801..55ef0c5bb 100644 --- a/src/ui/dialog/floating-behavior.cpp +++ b/src/ui/dialog/floating-behavior.cpp @@ -14,10 +14,6 @@ # include "config.h" #endif -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include <gtkmm/dialog.h> #include <gtkmm/stock.h> #include <glibmm/main.h> @@ -116,7 +112,7 @@ bool FloatingBehavior::_trans_timer (void) { } float goal, current; - goal = current = _d->get_opacity(); + current = _d->get_opacity(); if (_dialog_active.get_value()) { goal = _trans_focus; diff --git a/src/ui/dialog/font-substitution.cpp b/src/ui/dialog/font-substitution.cpp index ae03bdf0e..19506c6a3 100644 --- a/src/ui/dialog/font-substitution.cpp +++ b/src/ui/dialog/font-substitution.cpp @@ -10,10 +10,6 @@ # include <config.h> #endif -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include <gtkmm/messagedialog.h> #include <gtkmm/checkbutton.h> #include <gtkmm/scrolledwindow.h> @@ -70,19 +66,15 @@ FontSubstitution::checkFontSubstitutions(SPDocument* doc) int show_dlg = prefs->getInt("/options/font/substitutedlg", 0); if (show_dlg) { Glib::ustring out; - GSList *l = getFontReplacedItems(doc, &out); + std::vector<SPItem*> l = getFontReplacedItems(doc, &out); if (out.length() > 0) { show(out, l); } - if (l) { - g_slist_free(l); - l = NULL; - } } } void -FontSubstitution::show(Glib::ustring out, GSList *l) +FontSubstitution::show(Glib::ustring out, std::vector<SPItem*> &l) { Gtk::MessageDialog warning(_("\nSome fonts are not available and have been substituted."), false, Gtk::MESSAGE_INFO, Gtk::BUTTONS_OK, true); @@ -152,20 +144,18 @@ FontSubstitution::show(Glib::ustring out, GSList *l) * b. Build up a list of the objects rendered fonts - taken for the objects layout/spans * If there are fonts in a. that are not in b. then those fonts have been substituted. */ -GSList * FontSubstitution::getFontReplacedItems(SPDocument* doc, Glib::ustring *out) +std::vector<SPItem*> FontSubstitution::getFontReplacedItems(SPDocument* doc, Glib::ustring *out) { SPDesktop *desktop = SP_ACTIVE_DESKTOP; - GSList *allList = NULL; - GSList *outList = NULL; + std::vector<SPItem*> allList; + std::vector<SPItem*> outList,x,y; std::set<Glib::ustring> setErrors; std::set<Glib::ustring> setFontSpans; std::map<SPItem *, Glib::ustring> mapFontStyles; - allList = get_all_items(NULL, doc->getRoot(), desktop, false, false, true, NULL); - - for (GSList *i = allList; i != NULL; i = i->next) { - - SPItem *item = SP_ITEM(i->data); + allList = get_all_items(x, doc->getRoot(), desktop, false, false, true, y); + for(std::vector<SPItem*>::const_iterator i = allList.begin();i!=allList.end();i++){ + SPItem *item = *i; SPStyle *style = item->style; Glib::ustring family = ""; @@ -220,8 +210,8 @@ GSList * FontSubstitution::getFontReplacedItems(SPDocument* doc, Glib::ustring * } // Check if any document styles are not in the actual layout - std::map<SPItem *, Glib::ustring>::const_iterator mapIter; - for (mapIter = mapFontStyles.begin(); mapIter != mapFontStyles.end(); ++mapIter) { + std::map<SPItem *, Glib::ustring>::const_reverse_iterator mapIter; + for (mapIter = mapFontStyles.rbegin(); mapIter != mapFontStyles.rend(); ++mapIter) { SPItem *item = mapIter->first; Glib::ustring fonts = mapIter->second; @@ -254,7 +244,7 @@ GSList * FontSubstitution::getFontReplacedItems(SPDocument* doc, Glib::ustring * Glib::ustring err = Glib::ustring::compose( _("Font '%1' substituted with '%2'"), fonts.c_str(), subName.c_str()); setErrors.insert(err); - outList = g_slist_prepend (outList, item); + outList.push_back(item); } } diff --git a/src/ui/dialog/font-substitution.h b/src/ui/dialog/font-substitution.h index 1c445081b..0818d778c 100644 --- a/src/ui/dialog/font-substitution.h +++ b/src/ui/dialog/font-substitution.h @@ -14,6 +14,7 @@ #include <glibmm/ustring.h> +class SPItem; class SPDocument; namespace Inkscape { @@ -25,13 +26,13 @@ public: FontSubstitution(); virtual ~FontSubstitution(); void checkFontSubstitutions(SPDocument* doc); - void show(Glib::ustring out, GSList *l); + void show(Glib::ustring out, std::vector<SPItem*> &l); static FontSubstitution &getInstance() { return *new FontSubstitution(); } Glib::ustring getSubstituteFontName (Glib::ustring font); protected: - GSList *getFontReplacedItems(SPDocument* doc, Glib::ustring *out); + std::vector<SPItem*> getFontReplacedItems(SPDocument* doc, Glib::ustring *out); private: FontSubstitution(FontSubstitution const &d); diff --git a/src/ui/dialog/glyphs.cpp b/src/ui/dialog/glyphs.cpp index 2b9053da9..7ca277ea2 100644 --- a/src/ui/dialog/glyphs.cpp +++ b/src/ui/dialog/glyphs.cpp @@ -578,9 +578,10 @@ void GlyphsPanel::setTargetDesktop(SPDesktop *desktop) void GlyphsPanel::insertText() { SPItem *textItem = 0; - for (const GSList *item = targetDesktop->selection->itemList(); item; item = item->next ) { - if (SP_IS_TEXT(item->data) || SP_IS_FLOWTEXT(item->data)) { - textItem = SP_ITEM(item->data); + std::vector<SPItem*> itemlist=targetDesktop->selection->itemList(); + for(std::vector<SPItem*>::const_iterator i=itemlist.begin(); itemlist.end() != i; i++) { + if (SP_IS_TEXT(*i) || SP_IS_FLOWTEXT(*i)) { + textItem = *i; break; } } @@ -687,8 +688,9 @@ void GlyphsPanel::selectionModifiedCB(guint flags) void GlyphsPanel::calcCanInsert() { int items = 0; - for (const GSList *item = targetDesktop->selection->itemList(); item; item = item->next ) { - if (SP_IS_TEXT(item->data) || SP_IS_FLOWTEXT(item->data)) { + std::vector<SPItem*> itemlist=targetDesktop->selection->itemList(); + for(std::vector<SPItem*>::const_iterator i=itemlist.begin(); itemlist.end() != i; i++) { + if (SP_IS_TEXT(*i) || SP_IS_FLOWTEXT(*i)) { ++items; } } diff --git a/src/ui/dialog/grid-arrange-tab.cpp b/src/ui/dialog/grid-arrange-tab.cpp index d3ccb9bde..ccd23a572 100644 --- a/src/ui/dialog/grid-arrange-tab.cpp +++ b/src/ui/dialog/grid-arrange-tab.cpp @@ -48,7 +48,7 @@ * 0 *elem1 == *elem2 * >0 *elem1 goes after *elem2 */ -static int sp_compare_x_position(SPItem *first, SPItem *second) +static bool sp_compare_x_position(SPItem *first, SPItem *second) { using Geom::X; using Geom::Y; @@ -58,7 +58,7 @@ static int sp_compare_x_position(SPItem *first, SPItem *second) if ( !a || !b ) { // FIXME? - return 0; + return false; } double const a_height = a->dimensions()[Y]; @@ -76,40 +76,41 @@ static int sp_compare_x_position(SPItem *first, SPItem *second) } if (!a_in_b_vert) { - return -1; + return true; } if (a_in_b_vert && a->min()[X] > b->min()[X]) { - return 1; + return false; } if (a_in_b_vert && a->min()[X] < b->min()[X]) { - return -1; + return true; } - return 0; + return false; } /* * Sort items by their y co-ordinates. */ -static int sp_compare_y_position(SPItem *first, SPItem *second) +static bool sp_compare_y_position(SPItem *first, SPItem *second) { Geom::OptRect a = first->documentVisualBounds(); Geom::OptRect b = second->documentVisualBounds(); if ( !a || !b ) { // FIXME? - return 0; + return false; } if (a->min()[Geom::Y] > b->min()[Geom::Y]) { - return 1; + return false; } if (a->min()[Geom::Y] < b->min()[Geom::Y]) { - return -1; + return true; } - return 0; + return false; } + namespace Inkscape { namespace UI { namespace Dialog { @@ -168,10 +169,9 @@ void GridArrangeTab::arrange() desktop->getDocument()->ensureUpToDate(); Inkscape::Selection *selection = desktop->getSelection(); - const GSList *items = selection ? selection->itemList() : 0; - cnt=0; - for (; items != NULL; items = items->next) { - SPItem *item = SP_ITEM(items->data); + const std::vector<SPItem*> items = selection ? selection->itemList() : std::vector<SPItem*>(); + for(std::vector<SPItem*>::const_iterator i = items.begin();i!=items.end();i++){ + SPItem *item = *i; Geom::OptRect b = item->documentVisualBounds(); if (!b) { continue; @@ -198,20 +198,18 @@ void GridArrangeTab::arrange() // require the sorting done before we can calculate row heights etc. g_return_if_fail(selection); - const GSList *items2 = selection->itemList(); - GSList *rev = g_slist_copy(const_cast<GSList *>(items2)); - GSList *sorted = NULL; - rev = g_slist_sort(rev, (GCompareFunc) sp_compare_y_position); - sorted = g_slist_sort(rev, (GCompareFunc) sp_compare_x_position); + std::vector<SPItem*> sorted(selection->itemList()); + sort(sorted.begin(),sorted.end(),sp_compare_y_position); + sort(sorted.begin(),sorted.end(),sp_compare_x_position); // Calculate individual Row and Column sizes if necessary cnt=0; - const GSList *sizes = sorted; - for (; sizes != NULL; sizes = sizes->next) { - SPItem *item = SP_ITEM(sizes->data); + const std::vector<SPItem*> sizes(sorted); + for (std::vector<SPItem*>::const_iterator i = sizes.begin();i!=sizes.end();i++) { + SPItem *item = *i; Geom::OptRect b = item->documentVisualBounds(); if (b) { width = b->dimensions()[Geom::X]; @@ -308,12 +306,14 @@ g_print("\n row = %f col = %f selection x= %f selection y = %f", total_row_h } cnt=0; - for (row_cnt=0; ((sorted != NULL) && (row_cnt<NoOfRows)); row_cnt++) { + std::vector<SPItem*>::iterator it = sorted.begin(); + for (row_cnt=0; ((it != sorted.end()) && (row_cnt<NoOfRows)); row_cnt++) { GSList *current_row = NULL; - for (col_cnt = 0; ((sorted != NULL) && (col_cnt<NoOfCols)); col_cnt++) { - current_row = g_slist_append (current_row, sorted->data); - sorted = sorted->next; + col_cnt = 0; + for(;it!=sorted.end()&&col_cnt<NoOfCols;it++) { + current_row = g_slist_append (current_row, *it); + col_cnt++; } for (; current_row != NULL; current_row = current_row->next) { @@ -374,8 +374,8 @@ void GridArrangeTab::on_row_spinbutton_changed() Inkscape::Selection *selection = desktop ? desktop->selection : 0; g_return_if_fail( selection ); - GSList const *items = selection->itemList(); - int selcount = g_slist_length((GSList *)items); + std::vector<SPItem*> const items = selection->itemList(); + int selcount = items.size(); double PerCol = ceil(selcount / NoOfColsSpinner.get_value()); NoOfRowsSpinner.set_value(PerCol); @@ -400,8 +400,7 @@ void GridArrangeTab::on_col_spinbutton_changed() Inkscape::Selection *selection = desktop ? desktop->selection : 0; g_return_if_fail(selection); - GSList const *items = selection->itemList(); - int selcount = g_slist_length((GSList *)items); + int selcount = selection->itemList().size(); double PerRow = ceil(selcount / NoOfRowsSpinner.get_value()); NoOfColsSpinner.set_value(PerRow); @@ -538,10 +537,10 @@ void GridArrangeTab::updateSelection() updating = true; SPDesktop *desktop = Parent->getDesktop(); Inkscape::Selection *selection = desktop ? desktop->selection : 0; - GSList const *items = selection ? selection->itemList() : 0; + std::vector<SPItem*> const items = selection ? selection->itemList() : std::vector<SPItem*>(); - if (items) { - int selcount = g_slist_length((GSList *)items); + if (!items.empty()) { + int selcount = items.size(); if (NoOfColsSpinner.get_value() > 1 && NoOfRowsSpinner.get_value() > 1){ // Update the number of rows assuming number of columns wanted remains same. @@ -609,8 +608,7 @@ GridArrangeTab::GridArrangeTab(ArrangeDialog *parent) g_return_if_fail( selection ); int selcount = 1; if (!selection->isEmpty()) { - GSList const *items = selection->itemList(); - selcount = g_slist_length((GSList *)items); + selcount = selection->itemList().size(); } diff --git a/src/ui/dialog/guides.h b/src/ui/dialog/guides.h index 22bf5097a..4ff7b4fde 100644 --- a/src/ui/dialog/guides.h +++ b/src/ui/dialog/guides.h @@ -15,10 +15,6 @@ # include <config.h> #endif -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include <gtkmm/dialog.h> #if WITH_GTKMM_3_0 diff --git a/src/ui/dialog/icon-preview.cpp b/src/ui/dialog/icon-preview.cpp index b908a90cb..77f120e1a 100644 --- a/src/ui/dialog/icon-preview.cpp +++ b/src/ui/dialog/icon-preview.cpp @@ -17,10 +17,6 @@ # include <config.h> #endif -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include <gtkmm/buttonbox.h> #include <boost/scoped_ptr.hpp> @@ -316,7 +312,7 @@ void IconPreviewPanel::setDesktop( SPDesktop* desktop ) if ( this->desktop ) { docReplacedConn = this->desktop->connectDocumentReplaced(sigc::hide<0>(sigc::mem_fun(this, &IconPreviewPanel::setDocument))); if ( this->desktop->selection && Inkscape::Preferences::get()->getBool("/iconpreview/autoRefresh", true) ) { - selChangedConn = desktop->selection->connectChanged(sigc::hide(sigc::mem_fun(this, &IconPreviewPanel::queueRefresh))); + selChangedConn = this->desktop->selection->connectChanged(sigc::hide(sigc::mem_fun(this, &IconPreviewPanel::queueRefresh))); } } } @@ -366,16 +362,14 @@ void IconPreviewPanel::refreshPreview() if ( sel ) { //g_message("found a selection to play with"); - GSList const *items = sel->itemList(); - while ( items && !target ) { - SPItem* item = SP_ITEM( items->data ); + std::vector<SPItem*> const items = sel->itemList(); + for(std::vector<SPItem*>::const_iterator i=items.begin();!target && i!=items.end();i++){ + SPItem* item = *i; gchar const *id = item->getId(); if ( id ) { targetId = id; target = item; } - - items = g_slist_next(items); } } } diff --git a/src/ui/dialog/layer-properties.h b/src/ui/dialog/layer-properties.h index d114c6ba5..c75a7f190 100644 --- a/src/ui/dialog/layer-properties.h +++ b/src/ui/dialog/layer-properties.h @@ -16,10 +16,6 @@ # include <config.h> #endif -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include <gtkmm/dialog.h> #include <gtkmm/entry.h> #include <gtkmm/label.h> diff --git a/src/ui/dialog/layers.cpp b/src/ui/dialog/layers.cpp index 65351cb68..3f5e80f8d 100644 --- a/src/ui/dialog/layers.cpp +++ b/src/ui/dialog/layers.cpp @@ -713,13 +713,21 @@ bool LayersPanel::_handleDragDrop(const Glib::RefPtr<Gdk::DragContext>& /*contex */ void LayersPanel::_doTreeMove( ) { - if (_dnd_source ) { + if (_dnd_source && _dnd_source->getRepr() ) { + if(!_dnd_target){ + _dnd_source->doWriteTransform(_dnd_source->getRepr(), _dnd_source->document->getRoot()->i2doc_affine().inverse() * _dnd_source->i2doc_affine()); + }else{ + SPItem* parent = _dnd_into ? _dnd_target : dynamic_cast<SPItem*>(_dnd_target->parent); + if(parent){ + Geom::Affine move = parent->i2doc_affine().inverse() * _dnd_source->i2doc_affine(); + _dnd_source->doWriteTransform(_dnd_source->getRepr(), move); + } + } _dnd_source->moveTo(_dnd_target, _dnd_into); _selectLayer(_dnd_source); _dnd_source = NULL; DocumentUndo::done( _desktop->doc() , SP_VERB_NONE, - _("Moved layer")); - + _("Move layer")); } } diff --git a/src/ui/dialog/layers.h b/src/ui/dialog/layers.h index ae0ac6040..9cd2c3b92 100644 --- a/src/ui/dialog/layers.h +++ b/src/ui/dialog/layers.h @@ -16,10 +16,6 @@ # include <config.h> #endif -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include <gtkmm/box.h> #include <gtkmm/treeview.h> #include <gtkmm/treestore.h> diff --git a/src/ui/dialog/livepatheffect-add.h b/src/ui/dialog/livepatheffect-add.h index 99ead878c..c686e8365 100644 --- a/src/ui/dialog/livepatheffect-add.h +++ b/src/ui/dialog/livepatheffect-add.h @@ -11,14 +11,6 @@ #ifndef INKSCAPE_DIALOG_LIVEPATHEFFECT_ADD_H #define INKSCAPE_DIALOG_LIVEPATHEFFECT_ADD_H -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif - -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include <gtkmm/dialog.h> #include <gtkmm/liststore.h> #include <gtkmm/treeview.h> diff --git a/src/ui/dialog/lpe-fillet-chamfer-properties.cpp b/src/ui/dialog/lpe-fillet-chamfer-properties.cpp index 7e5c17133..5ccee103c 100644 --- a/src/ui/dialog/lpe-fillet-chamfer-properties.cpp +++ b/src/ui/dialog/lpe-fillet-chamfer-properties.cpp @@ -8,10 +8,6 @@ #include <config.h> #endif -#if GLIBMM_DISABLE_DEPRECATED &&HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include <gtkmm.h> #include "lpe-fillet-chamfer-properties.h" #include <boost/lexical_cast.hpp> @@ -31,7 +27,7 @@ #include "selection-chemistry.h" #include "ui/icon-names.h" #include "ui/widget/imagetoggler.h" -#include "util/units.h" +#include "live_effects/parameter/parameter.h" #include <cmath> //#include "event-context.h" @@ -52,7 +48,7 @@ FilletChamferPropertiesDialog::FilletChamferPropertiesDialog() _fillet_chamfer_position_numeric.set_digits(4); _fillet_chamfer_position_numeric.set_increments(1,1); //todo: get tha max aloable infinity freeze the widget - _fillet_chamfer_position_numeric.set_range(0., 999999999999999999.); + _fillet_chamfer_position_numeric.set_range(0., SCALARPARAM_G_MAXDOUBLE); _fillet_chamfer_position_label.set_label(_("Radius (pixels):")); _fillet_chamfer_position_label.set_alignment(1.0, 0.5); @@ -64,7 +60,7 @@ FilletChamferPropertiesDialog::FilletChamferPropertiesDialog() _fillet_chamfer_chamfer_subdivisions.set_digits(0); _fillet_chamfer_chamfer_subdivisions.set_increments(1,1); //todo: get tha max aloable infinity freeze the widget - _fillet_chamfer_chamfer_subdivisions.set_range(0, 999999999999999999.0); + _fillet_chamfer_chamfer_subdivisions.set_range(0, SCALARPARAM_G_MAXDOUBLE); _fillet_chamfer_chamfer_subdivisions_label.set_label(_("Chamfer subdivisions:")); _fillet_chamfer_chamfer_subdivisions_label.set_alignment(1.0, 0.5); @@ -126,18 +122,14 @@ void FilletChamferPropertiesDialog::showDialog( SPDesktop *desktop, Geom::Point knotpoint, const Inkscape::LivePathEffect:: FilletChamferPointArrayParamKnotHolderEntity *pt, - const gchar *unit, bool use_distance, - bool aprox_radius, - Glib::ustring documentUnit) + bool aprox_radius) { FilletChamferPropertiesDialog *dialog = new FilletChamferPropertiesDialog(); dialog->_set_desktop(desktop); - dialog->_set_unit(unit); dialog->_set_use_distance(use_distance); dialog->_set_aprox(aprox_radius); - dialog->_set_document_unit(documentUnit); dialog->_set_knot_point(knotpoint); dialog->_set_pt(pt); @@ -172,7 +164,6 @@ void FilletChamferPropertiesDialog::_apply() } d_pos = _index + (d_pos / 100); } else { - d_pos = Inkscape::Util::Quantity::convert(d_pos, unit, document_unit); d_pos = d_pos * -1; } _knotpoint->knot_set_offset(Geom::Point(d_pos, d_width)); @@ -222,11 +213,9 @@ void FilletChamferPropertiesDialog::_set_knot_point(Geom::Point knotpoint) _fillet_chamfer_position_label.set_label(_("Position (%):")); } else { _flexible = false; - std::string posConcat = Glib::ustring::compose (_("%1 (%2):"), distance_or_radius, unit); + std::string posConcat = Glib::ustring::compose (_("%1:"), distance_or_radius); _fillet_chamfer_position_label.set_label(_(posConcat.c_str())); position = knotpoint[Geom::X] * -1; - - position = Inkscape::Util::Quantity::convert(position, document_unit, unit); } _fillet_chamfer_position_numeric.set_value(position); if (knotpoint.y() == 1) { @@ -251,16 +240,6 @@ void FilletChamferPropertiesDialog::_set_pt( pt); } -void FilletChamferPropertiesDialog::_set_unit(const gchar *abbr) -{ - unit = abbr; -} - -void FilletChamferPropertiesDialog::_set_document_unit(Glib::ustring abbr) -{ - document_unit = abbr; -} - void FilletChamferPropertiesDialog::_set_use_distance(bool use_knot_distance) { use_distance = use_knot_distance; diff --git a/src/ui/dialog/lpe-fillet-chamfer-properties.h b/src/ui/dialog/lpe-fillet-chamfer-properties.h index 870a1734f..99494bd63 100644 --- a/src/ui/dialog/lpe-fillet-chamfer-properties.h +++ b/src/ui/dialog/lpe-fillet-chamfer-properties.h @@ -30,10 +30,8 @@ public: static void showDialog(SPDesktop *desktop, Geom::Point knotpoint, const Inkscape::LivePathEffect:: FilletChamferPointArrayParamKnotHolderEntity *pt, - const gchar *unit, bool use_distance, - bool aprox_radius, - Glib::ustring documentUnit); + bool aprox_radius); protected: @@ -68,15 +66,11 @@ protected: void _set_desktop(SPDesktop *desktop); void _set_pt(const Inkscape::LivePathEffect:: FilletChamferPointArrayParamKnotHolderEntity *pt); - void _set_unit(const gchar *abbr); - void _set_document_unit(Glib::ustring abbr); void _set_use_distance(bool use_knot_distance); void _set_aprox(bool aprox_radius); void _apply(); void _close(); bool _flexible; - const gchar *unit; - Glib::ustring document_unit; bool use_distance; bool aprox; void _set_knot_point(Geom::Point knotpoint); diff --git a/src/ui/dialog/lpe-powerstroke-properties.cpp b/src/ui/dialog/lpe-powerstroke-properties.cpp index a9e57970d..cfc972547 100644 --- a/src/ui/dialog/lpe-powerstroke-properties.cpp +++ b/src/ui/dialog/lpe-powerstroke-properties.cpp @@ -17,10 +17,6 @@ # include <config.h> #endif -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include "lpe-powerstroke-properties.h" #include <boost/lexical_cast.hpp> #include <gtkmm/stock.h> @@ -40,6 +36,7 @@ #include "selection-chemistry.h" #include "ui/icon-names.h" #include "ui/widget/imagetoggler.h" +#include "live_effects/parameter/parameter.h" //#include "event-context.h" namespace Inkscape { @@ -56,10 +53,16 @@ PowerstrokePropertiesDialog::PowerstrokePropertiesDialog() // Layer name widgets _powerstroke_position_entry.set_activates_default(true); + _powerstroke_position_entry.set_digits(4); + _powerstroke_position_entry.set_increments(1,1); + _powerstroke_position_entry.set_range(-SCALARPARAM_G_MAXDOUBLE, SCALARPARAM_G_MAXDOUBLE); _powerstroke_position_label.set_label(_("Position:")); _powerstroke_position_label.set_alignment(1.0, 0.5); _powerstroke_width_entry.set_activates_default(true); + _powerstroke_width_entry.set_digits(4); + _powerstroke_width_entry.set_increments(1,1); + _powerstroke_width_entry.set_range(-SCALARPARAM_G_MAXDOUBLE, SCALARPARAM_G_MAXDOUBLE); _powerstroke_width_label.set_label(_("Width:")); _powerstroke_width_label.set_alignment(1.0, 0.5); @@ -130,12 +133,9 @@ void PowerstrokePropertiesDialog::showDialog(SPDesktop *desktop, Geom::Point kno void PowerstrokePropertiesDialog::_apply() { - std::istringstream i_pos(_powerstroke_position_entry.get_text()); - std::istringstream i_width(_powerstroke_width_entry.get_text()); - double d_pos, d_width; - if ((i_pos >> d_pos) && i_width >> d_width) { - _knotpoint->knot_set_offset(Geom::Point(d_pos, d_width)); - } + double d_pos = _powerstroke_position_entry.get_value(); + double d_width = _powerstroke_width_entry.get_value(); + _knotpoint->knot_set_offset(Geom::Point(d_pos, d_width)); _close(); } @@ -175,8 +175,8 @@ void PowerstrokePropertiesDialog::_handleButtonEvent(GdkEventButton* event) void PowerstrokePropertiesDialog::_setKnotPoint(Geom::Point knotpoint) { - _powerstroke_position_entry.set_text(boost::lexical_cast<std::string>(knotpoint.x())); - _powerstroke_width_entry.set_text(boost::lexical_cast<std::string>(knotpoint.y())); + _powerstroke_position_entry.set_value(knotpoint.x()); + _powerstroke_width_entry.set_value(knotpoint.y()); } void PowerstrokePropertiesDialog::_setPt(const Inkscape::LivePathEffect::PowerStrokePointArrayParamKnotHolderEntity *pt) diff --git a/src/ui/dialog/lpe-powerstroke-properties.h b/src/ui/dialog/lpe-powerstroke-properties.h index c53eac0d9..1e4c1df5b 100644 --- a/src/ui/dialog/lpe-powerstroke-properties.h +++ b/src/ui/dialog/lpe-powerstroke-properties.h @@ -13,15 +13,7 @@ #define INKSCAPE_DIALOG_POWERSTROKE_PROPERTIES_H #include <2geom/point.h> -#include <gtkmm/dialog.h> -#include <gtkmm/entry.h> -#include <gtkmm/label.h> -#include <gtkmm/table.h> -#include <gtkmm/combobox.h> -#include <gtkmm/liststore.h> -#include <gtkmm/treeview.h> -#include <gtkmm/treestore.h> -#include <gtkmm/scrolledwindow.h> +#include <gtkmm.h> #include "live_effects/parameter/powerstrokepointarray.h" class SPDesktop; @@ -45,9 +37,9 @@ protected: Inkscape::LivePathEffect::PowerStrokePointArrayParamKnotHolderEntity *_knotpoint; Gtk::Label _powerstroke_position_label; - Gtk::Entry _powerstroke_position_entry; + Gtk::SpinButton _powerstroke_position_entry; Gtk::Label _powerstroke_width_label; - Gtk::Entry _powerstroke_width_entry; + Gtk::SpinButton _powerstroke_width_entry; Gtk::Table _layout_table; bool _position_visible; diff --git a/src/ui/dialog/messages.h b/src/ui/dialog/messages.h index 54ca84f47..6ed246ece 100644 --- a/src/ui/dialog/messages.h +++ b/src/ui/dialog/messages.h @@ -16,14 +16,6 @@ #ifndef INKSCAPE_UI_DIALOG_MESSAGES_H #define INKSCAPE_UI_DIALOG_MESSAGES_H -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif - -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include <gtkmm/box.h> #include <gtkmm/textview.h> #include <gtkmm/button.h> diff --git a/src/ui/dialog/object-attributes.cpp b/src/ui/dialog/object-attributes.cpp index f43a15225..1bc570f43 100644 --- a/src/ui/dialog/object-attributes.cpp +++ b/src/ui/dialog/object-attributes.cpp @@ -127,7 +127,7 @@ void ObjectAttributes::widget_setup (void) blocked = true; // CPPIFY - SPObject *obj = SP_OBJECT(item); //to get the selected item + SPObject *obj = item; //to get the selected item // GObjectClass *klass = G_OBJECT_GET_CLASS(obj); //to deduce the object's type // GType type = G_TYPE_FROM_CLASS(klass); const SPAttrDesc *desc; diff --git a/src/ui/dialog/object-properties.cpp b/src/ui/dialog/object-properties.cpp index dfe211e94..75430ed44 100644 --- a/src/ui/dialog/object-properties.cpp +++ b/src/ui/dialog/object-properties.cpp @@ -467,14 +467,14 @@ void ObjectProperties::_labelChanged() gchar *id = g_strdup(_entry_id.get_text().c_str()); g_strcanon(id, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.:", '_'); if (strcmp(id, item->getId()) == 0) { - _label_id.set_markup_with_mnemonic(_("_ID:")); + _label_id.set_markup_with_mnemonic(_("_ID:") + Glib::ustring(" ")); } else if (!*id || !isalnum (*id)) { _label_id.set_text(_("Id invalid! ")); } else if (SP_ACTIVE_DOCUMENT->getObjectById(id) != NULL) { _label_id.set_text(_("Id exists! ")); } else { SPException ex; - _label_id.set_markup_with_mnemonic(_("_ID:")); + _label_id.set_markup_with_mnemonic(_("_ID:") + Glib::ustring(" ")); SP_EXCEPTION_INIT(&ex); item->setAttribute("id", id, &ex); DocumentUndo::done(SP_ACTIVE_DOCUMENT, SP_VERB_DIALOG_ITEM, _("Set object ID")); @@ -529,10 +529,12 @@ void ObjectProperties::_imageRenderingChanged() SPCSSAttr *css = sp_repr_css_attr_new(); sp_repr_css_set_property(css, "image-rendering", scale.c_str()); Inkscape::XML::Node *image_node = item->getRepr(); - if( image_node ) { + if (image_node) { sp_repr_css_change(image_node, css, "style"); + DocumentUndo::done(SP_ACTIVE_DOCUMENT, SP_VERB_DIALOG_ITEM, + _("Set image rendering option")); } - sp_repr_css_attr_unref( css ); + sp_repr_css_attr_unref(css); _blocked = false; } diff --git a/src/ui/dialog/objects.cpp b/src/ui/dialog/objects.cpp index 9db0285d7..835ecf35b 100644 --- a/src/ui/dialog/objects.cpp +++ b/src/ui/dialog/objects.cpp @@ -45,6 +45,7 @@ #include "style.h" #include "ui/tools-switch.h" #include "ui/icon-names.h" +#include "ui/selected-color.h" #include "ui/widget/imagetoggler.h" #include "ui/widget/layertypeicon.h" #include "ui/widget/insertordericon.h" @@ -53,7 +54,7 @@ #include "ui/tools/node-tool.h" #include "ui/tools/tool-base.h" #include "verbs.h" -#include "widgets/sp-color-notebook.h" +#include "ui/widget/color-notebook.h" #include "widgets/icon.h" #include "xml/node.h" #include "xml/node-observer.h" @@ -477,15 +478,15 @@ void ObjectsPanel::_objectsSelected( Selection *sel ) { _selectedConnection.block(); _tree.get_selection()->unselect_all(); SPItem *item = NULL; - for (const GSList * iter = sel->itemList(); iter != NULL; iter = iter->next) - { - item = reinterpret_cast<SPItem *>(iter->data); + std::vector<SPItem*> const items = sel->itemList(); + for(std::vector<SPItem*>::const_iterator i=items.begin(); i!=items.end();i++){ + item = *i; if (setOpacity) { _setCompositingValues(item); setOpacity = false; } - _store->foreach(sigc::bind<SPItem *, bool>( sigc::mem_fun(*this, &ObjectsPanel::_checkForSelected), item, iter->next == NULL)); + _store->foreach(sigc::bind<SPItem *, bool>( sigc::mem_fun(*this, &ObjectsPanel::_checkForSelected), item, (*i)==items.back())); } if (!item) { if (_desktop->currentLayer() && SP_IS_ITEM(_desktop->currentLayer())) { @@ -928,12 +929,12 @@ bool ObjectsPanel::_handleButtonEvent(GdkEventButton* event) //If the current item is not selected, store only it in the highlight source _storeHighlightTarget(iter); } - if (_colorSelector) + if (_selectedColor) { //Set up the color selector SPColor color; color.set( row[_model->_colHighlight] ); - _colorSelector->base->setColorAlpha(color, SP_RGBA32_A_F(row[_model->_colHighlight])); + _selectedColor->setColorAlpha(color, SP_RGBA32_A_F(row[_model->_colHighlight])); } //Show the color selector dialog _colorSelectorDialog.show(); @@ -1440,17 +1441,16 @@ void ObjectsPanel::_setExpanded(const Gtk::TreeModel::iterator& iter, const Gtk: * @param csel Color selector * @param cp Objects panel */ -void sp_highlight_picker_color_mod(SPColorSelector *csel, GObject * cp) +void ObjectsPanel::_highlightPickerColorMod() { SPColor color; float alpha = 0; - csel->base->getColorAlpha(color, alpha); + _selectedColor->colorAlpha(color, alpha); + guint32 rgba = color.toRGBA32( alpha ); - - ObjectsPanel *ptr = reinterpret_cast<ObjectsPanel *>(cp); //Set the highlight color for all items in the _highlight_target (all selected items) - for (std::vector<SPItem *>::iterator iter = ptr->_highlight_target.begin(); iter != ptr->_highlight_target.end(); ++iter) + for (std::vector<SPItem *>::iterator iter = _highlight_target.begin(); iter != _highlight_target.end(); ++iter) { SPItem * target = *iter; target->setHighlightColor(rgba); @@ -1614,6 +1614,12 @@ ObjectsPanel::ObjectsPanel() : _pending(0), _toggleEvent(0), _defer_target(), + _visibleHeader(_("V")), + _lockHeader(_("L")), + _typeHeader(_("T")), + _clipmaskHeader(_("CM")), + _highlightHeader(_("HL")), + _nameHeader(_("Label")), _composite_vbox(false, 0), _opacity_vbox(false, 0), _opacity_label(_("Opacity:")), @@ -1641,7 +1647,7 @@ ObjectsPanel::ObjectsPanel() : //Set up the tree _tree.set_model( _store ); - _tree.set_headers_visible(false); + _tree.set_headers_visible(true); _tree.set_reorderable(true); _tree.enable_model_drag_dest (Gdk::ACTION_MOVE); @@ -1654,6 +1660,10 @@ ObjectsPanel::ObjectsPanel() : Gtk::TreeViewColumn* col = _tree.get_column(visibleColNum); if ( col ) { col->add_attribute( eyeRenderer->property_active(), _model->_colVisible ); + // In order to get tooltips on header, we must create our own label. + _visibleHeader.set_tooltip_text(_("Toggle visibility of Layer, Group, or Object.")); + _visibleHeader.show(); + col->set_widget( _visibleHeader ); } //Locked @@ -1664,6 +1674,9 @@ ObjectsPanel::ObjectsPanel() : col = _tree.get_column(lockedColNum); if ( col ) { col->add_attribute( renderer->property_active(), _model->_colLocked ); + _lockHeader.set_tooltip_text(_("Toggle lock of Layer, Group, or Object.")); + _lockHeader.show(); + col->set_widget( _lockHeader ); } //Type @@ -1673,6 +1686,9 @@ ObjectsPanel::ObjectsPanel() : col = _tree.get_column(typeColNum); if ( col ) { col->add_attribute( typeRenderer->property_active(), _model->_colType ); + _typeHeader.set_tooltip_text(_("Type: Layer, Group, or Object. Clicking on Layer or Group icon, toggles between the two types.")); + _typeHeader.show(); + col->set_widget( _typeHeader ); } //Insert order (LiamW: unused) @@ -1689,6 +1705,9 @@ ObjectsPanel::ObjectsPanel() : col = _tree.get_column(clipColNum); if ( col ) { col->add_attribute( clipRenderer->property_active(), _model->_colClipMask ); + _clipmaskHeader.set_tooltip_text(_("Is object clipped and/or masked?")); + _clipmaskHeader.show(); + col->set_widget( _clipmaskHeader ); } //Highlight @@ -1697,13 +1716,21 @@ ObjectsPanel::ObjectsPanel() : col = _tree.get_column(highlightColNum); if ( col ) { col->add_attribute( highlightRenderer->property_active(), _model->_colHighlight ); + _highlightHeader.set_tooltip_text(_("Highlight color of outline in Node tool. Click to set. If alpha is zero, use inherited color.")); + _highlightHeader.show(); + col->set_widget( _highlightHeader ); } //Label _text_renderer = Gtk::manage(new Gtk::CellRendererText()); int nameColNum = _tree.append_column("Name", *_text_renderer) - 1; _name_column = _tree.get_column(nameColNum); - _name_column->add_attribute(_text_renderer->property_text(), _model->_colLabel); + if( _name_column ) { + _name_column->add_attribute(_text_renderer->property_text(), _model->_colLabel); + _nameHeader.set_tooltip_text(_("Layer/Group/Object label (inkscape:label). Double-click to set. Default value is object 'id'.")); + _nameHeader.show(); + _name_column->set_widget( _nameHeader ); + } //Set the expander and search columns _tree.set_expander_column( *_tree.get_column(nameColNum) ); @@ -1858,46 +1885,46 @@ ObjectsPanel::ObjectsPanel() : //Set up the pop-up menu // ------------------------------------------------------- { - _watching.push_back( &_addPopupItem( targetDesktop, SP_VERB_LAYER_RENAME, 0, "Rename", (int)BUTTON_RENAME ) ); - _watching.push_back( &_addPopupItem( targetDesktop, SP_VERB_EDIT_DUPLICATE, 0, "Duplicate", (int)BUTTON_DUPLICATE ) ); - _watching.push_back( &_addPopupItem( targetDesktop, SP_VERB_LAYER_NEW, 0, "New", (int)BUTTON_NEW ) ); + _watching.push_back( &_addPopupItem( targetDesktop, SP_VERB_LAYER_RENAME, 0, _("Rename"), (int)BUTTON_RENAME ) ); + _watching.push_back( &_addPopupItem( targetDesktop, SP_VERB_EDIT_DUPLICATE, 0, _("Duplicate"), (int)BUTTON_DUPLICATE ) ); + _watching.push_back( &_addPopupItem( targetDesktop, SP_VERB_LAYER_NEW, 0, _("New"), (int)BUTTON_NEW ) ); _popupMenu.append(*Gtk::manage(new Gtk::SeparatorMenuItem())); - _watching.push_back( &_addPopupItem( targetDesktop, SP_VERB_LAYER_SOLO, 0, "Solo", (int)BUTTON_SOLO ) ); - _watching.push_back( &_addPopupItem( targetDesktop, SP_VERB_LAYER_SHOW_ALL, 0, "Show All", (int)BUTTON_SHOW_ALL ) ); - _watching.push_back( &_addPopupItem( targetDesktop, SP_VERB_LAYER_HIDE_ALL, 0, "Hide All", (int)BUTTON_HIDE_ALL ) ); + _watching.push_back( &_addPopupItem( targetDesktop, SP_VERB_LAYER_SOLO, 0, _("Solo"), (int)BUTTON_SOLO ) ); + _watching.push_back( &_addPopupItem( targetDesktop, SP_VERB_LAYER_SHOW_ALL, 0, _("Show All"), (int)BUTTON_SHOW_ALL ) ); + _watching.push_back( &_addPopupItem( targetDesktop, SP_VERB_LAYER_HIDE_ALL, 0, _("Hide All"), (int)BUTTON_HIDE_ALL ) ); _popupMenu.append(*Gtk::manage(new Gtk::SeparatorMenuItem())); - _watching.push_back( &_addPopupItem( targetDesktop, SP_VERB_LAYER_LOCK_OTHERS, 0, "Lock Others", (int)BUTTON_LOCK_OTHERS ) ); - _watching.push_back( &_addPopupItem( targetDesktop, SP_VERB_LAYER_LOCK_ALL, 0, "Lock All", (int)BUTTON_LOCK_ALL ) ); - _watching.push_back( &_addPopupItem( targetDesktop, SP_VERB_LAYER_UNLOCK_ALL, 0, "Unlock All", (int)BUTTON_UNLOCK_ALL ) ); + _watching.push_back( &_addPopupItem( targetDesktop, SP_VERB_LAYER_LOCK_OTHERS, 0, _("Lock Others"), (int)BUTTON_LOCK_OTHERS ) ); + _watching.push_back( &_addPopupItem( targetDesktop, SP_VERB_LAYER_LOCK_ALL, 0, _("Lock All"), (int)BUTTON_LOCK_ALL ) ); + _watching.push_back( &_addPopupItem( targetDesktop, SP_VERB_LAYER_UNLOCK_ALL, 0, _("Unlock All"), (int)BUTTON_UNLOCK_ALL ) ); _popupMenu.append(*Gtk::manage(new Gtk::SeparatorMenuItem())); - _watchingNonTop.push_back( &_addPopupItem( targetDesktop, SP_VERB_SELECTION_RAISE, GTK_STOCK_GO_UP, "Up", (int)BUTTON_UP ) ); - _watchingNonBottom.push_back( &_addPopupItem( targetDesktop, SP_VERB_SELECTION_LOWER, GTK_STOCK_GO_DOWN, "Down", (int)BUTTON_DOWN ) ); + _watchingNonTop.push_back( &_addPopupItem( targetDesktop, SP_VERB_SELECTION_RAISE, GTK_STOCK_GO_UP, _("Up"), (int)BUTTON_UP ) ); + _watchingNonBottom.push_back( &_addPopupItem( targetDesktop, SP_VERB_SELECTION_LOWER, GTK_STOCK_GO_DOWN, _("Down"), (int)BUTTON_DOWN ) ); _popupMenu.append(*Gtk::manage(new Gtk::SeparatorMenuItem())); - _watching.push_back( &_addPopupItem( targetDesktop, SP_VERB_SELECTION_GROUP, 0, "Group", (int)BUTTON_GROUP ) ); - _watching.push_back( &_addPopupItem( targetDesktop, SP_VERB_SELECTION_UNGROUP, 0, "Ungroup", (int)BUTTON_UNGROUP ) ); + _watching.push_back( &_addPopupItem( targetDesktop, SP_VERB_SELECTION_GROUP, 0, _("Group"), (int)BUTTON_GROUP ) ); + _watching.push_back( &_addPopupItem( targetDesktop, SP_VERB_SELECTION_UNGROUP, 0, _("Ungroup"), (int)BUTTON_UNGROUP ) ); _popupMenu.append(*Gtk::manage(new Gtk::SeparatorMenuItem())); - _watching.push_back( &_addPopupItem( targetDesktop, SP_VERB_OBJECT_SET_CLIPPATH, 0, "Set Clip", (int)BUTTON_SETCLIP ) ); + _watching.push_back( &_addPopupItem( targetDesktop, SP_VERB_OBJECT_SET_CLIPPATH, 0, _("Set Clip"), (int)BUTTON_SETCLIP ) ); - _watching.push_back( &_addPopupItem( targetDesktop, SP_VERB_OBJECT_CREATE_CLIP_GROUP, 0, "Create Clip Group", (int)BUTTON_CLIPGROUP ) ); + _watching.push_back( &_addPopupItem( targetDesktop, SP_VERB_OBJECT_CREATE_CLIP_GROUP, 0, _("Create Clip Group"), (int)BUTTON_CLIPGROUP ) ); //will never be implemented //_watching.push_back( &_addPopupItem( targetDesktop, SP_VERB_OBJECT_SET_INVERSE_CLIPPATH, 0, "Set Inverse Clip", (int)BUTTON_SETINVCLIP ) ); - _watching.push_back( &_addPopupItem( targetDesktop, SP_VERB_OBJECT_UNSET_CLIPPATH, 0, "Unset Clip", (int)BUTTON_UNSETCLIP ) ); + _watching.push_back( &_addPopupItem( targetDesktop, SP_VERB_OBJECT_UNSET_CLIPPATH, 0, _("Unset Clip"), (int)BUTTON_UNSETCLIP ) ); _popupMenu.append(*Gtk::manage(new Gtk::SeparatorMenuItem())); - _watching.push_back( &_addPopupItem( targetDesktop, SP_VERB_OBJECT_SET_MASK, 0, "Set Mask", (int)BUTTON_SETMASK ) ); - _watching.push_back( &_addPopupItem( targetDesktop, SP_VERB_OBJECT_UNSET_MASK, 0, "Unset Mask", (int)BUTTON_UNSETMASK ) ); + _watching.push_back( &_addPopupItem( targetDesktop, SP_VERB_OBJECT_SET_MASK, 0, _("Set Mask"), (int)BUTTON_SETMASK ) ); + _watching.push_back( &_addPopupItem( targetDesktop, SP_VERB_OBJECT_UNSET_MASK, 0, _("Unset Mask"), (int)BUTTON_UNSETMASK ) ); _popupMenu.show_all_children(); } @@ -1922,18 +1949,16 @@ ObjectsPanel::ObjectsPanel() : _colorSelectorDialog.set_title (_("Select Highlight Color")); _colorSelectorDialog.set_border_width (4); _colorSelectorDialog.property_modal() = true; - _colorSelector = SP_COLOR_SELECTOR(sp_color_selector_new(SP_TYPE_COLOR_NOTEBOOK)); + _selectedColor.reset(new Inkscape::UI::SelectedColor); + Gtk::Widget *color_selector = Gtk::manage(new Inkscape::UI::Widget::ColorNotebook(*_selectedColor)); _colorSelectorDialog.get_vbox()->pack_start ( - *Glib::wrap(&_colorSelector->vbox), true, true, 0); + *color_selector, true, true, 0); - g_signal_connect(G_OBJECT(_colorSelector), "dragged", - G_CALLBACK(sp_highlight_picker_color_mod), (void *)this); - g_signal_connect(G_OBJECT(_colorSelector), "released", - G_CALLBACK(sp_highlight_picker_color_mod), (void *)this); - g_signal_connect(G_OBJECT(_colorSelector), "changed", - G_CALLBACK(sp_highlight_picker_color_mod), (void *)this); + _selectedColor->signal_dragged.connect(sigc::mem_fun(*this, &ObjectsPanel::_highlightPickerColorMod)); + _selectedColor->signal_released.connect(sigc::mem_fun(*this, &ObjectsPanel::_highlightPickerColorMod)); + _selectedColor->signal_changed.connect(sigc::mem_fun(*this, &ObjectsPanel::_highlightPickerColorMod)); - gtk_widget_show(GTK_WIDGET(_colorSelector)); + color_selector->show(); setDesktop( targetDesktop ); @@ -1951,7 +1976,6 @@ ObjectsPanel::~ObjectsPanel() { //Close the highlight selection dialog _colorSelectorDialog.hide(); - _colorSelector = NULL; //Set the desktop to null, which will disconnect all object watchers setDesktop(NULL); diff --git a/src/ui/dialog/objects.h b/src/ui/dialog/objects.h index 74c2382ac..9b9a6025a 100644 --- a/src/ui/dialog/objects.h +++ b/src/ui/dialog/objects.h @@ -16,10 +16,7 @@ # include <config.h> #endif -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -# include <glibmm/threads.h> -#endif - +#include <boost/scoped_ptr.hpp> #include <gtkmm/box.h> #include <gtkmm/treeview.h> #include <gtkmm/treestore.h> @@ -40,6 +37,9 @@ struct SPColorSelector; namespace Inkscape { namespace UI { + +class SelectedColor; + namespace Dialog { @@ -147,7 +147,14 @@ private: Gtk::Menu _popupMenu; Inkscape::UI::Widget::SpinButton _spinBtn; Gtk::VBox _page; - + + Gtk::Label _visibleHeader; + Gtk::Label _lockHeader; + Gtk::Label _typeHeader; + Gtk::Label _clipmaskHeader; + Gtk::Label _highlightHeader; + Gtk::Label _nameHeader; + /* Composite Settings */ Gtk::VBox _composite_vbox; Gtk::VBox _opacity_vbox; @@ -170,8 +177,7 @@ private: Gtk::Alignment _blur_alignment; Gtk::Dialog _colorSelectorDialog; - SPColorSelector *_colorSelector; - + boost::scoped_ptr<Inkscape::UI::SelectedColor> _selectedColor; //Methods: @@ -237,7 +243,7 @@ private: void setupDialog(const Glib::ustring &title); - friend void sp_highlight_picker_color_mod(SPColorSelector *csel, GObject *cp); + void _highlightPickerColorMod(); }; diff --git a/src/ui/dialog/ocaldialogs.h b/src/ui/dialog/ocaldialogs.h index bd028c145..6ceceb9ef 100644 --- a/src/ui/dialog/ocaldialogs.h +++ b/src/ui/dialog/ocaldialogs.h @@ -17,10 +17,6 @@ # include <config.h> #endif -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - //Gtk includes #include <gtkmm/box.h> #include <gtkmm/eventbox.h> diff --git a/src/ui/dialog/pixelartdialog.cpp b/src/ui/dialog/pixelartdialog.cpp index 5113f172a..760391df6 100644 --- a/src/ui/dialog/pixelartdialog.cpp +++ b/src/ui/dialog/pixelartdialog.cpp @@ -372,12 +372,12 @@ void PixelArtDialogImpl::vectorize() return; } - for ( GSList const *list = desktop->selection->itemList() ; list - ; list = list->next ) { - if ( !SP_IS_IMAGE(list->data) ) + std::vector<SPItem*> const items = desktop->selection->itemList(); + for(std::vector<SPItem*>::const_iterator i=items.begin(); i!=items.end();i++){ + if ( !SP_IS_IMAGE(*i) ) continue; - SPImage *img = SP_IMAGE(list->data); + SPImage *img = SP_IMAGE(*i); Input input; input.pixbuf = Glib::wrap(img->pixbuf->getPixbufRaw(), true); input.x = img->x; diff --git a/src/ui/dialog/polar-arrange-tab.cpp b/src/ui/dialog/polar-arrange-tab.cpp index 281958998..af1386e27 100644 --- a/src/ui/dialog/polar-arrange-tab.cpp +++ b/src/ui/dialog/polar-arrange-tab.cpp @@ -297,19 +297,18 @@ static void moveToPoint(int anchor, SPItem *item, Geom::Point p) void PolarArrangeTab::arrange() { Inkscape::Selection *selection = parent->getDesktop()->getSelection(); - const GSList *items, *tmp; - tmp = items = selection->itemList(); + const std::vector<SPItem*> tmp(selection->itemList()); SPGenericEllipse *referenceEllipse = NULL; // Last ellipse in selection bool arrangeOnEllipse = !arrangeOnParametersRadio.get_active(); bool arrangeOnFirstEllipse = arrangeOnEllipse && arrangeOnFirstCircleRadio.get_active(); int count = 0; - while(tmp) + for(std::vector<SPItem*>::const_iterator i=tmp.begin();i!=tmp.end();i++) { if(arrangeOnEllipse) { - SPItem *item = SP_ITEM(tmp->data); + SPItem *item = *i; if(arrangeOnFirstEllipse) { @@ -322,7 +321,6 @@ void PolarArrangeTab::arrange() referenceEllipse = SP_GENERICELLIPSE(item); } } - tmp = tmp->next; ++count; } @@ -374,11 +372,10 @@ void PolarArrangeTab::arrange() Geom::Point realCenter = Geom::Point(cx, cy) * transformation; - tmp = items; int i = 0; - while(tmp) + for(std::vector<SPItem*>::const_iterator it=tmp.begin();it!=tmp.end();it++) { - SPItem *item = SP_ITEM(tmp->data); + SPItem *item = *it; // Ignore the reference ellipse if any if(item != referenceEllipse) @@ -396,7 +393,6 @@ void PolarArrangeTab::arrange() ++i; } - tmp = tmp->next; } DocumentUndo::done(parent->getDesktop()->getDocument(), SP_VERB_SELECTION_ARRANGE, diff --git a/src/ui/dialog/print.cpp b/src/ui/dialog/print.cpp index ad979b570..c44d645a5 100644 --- a/src/ui/dialog/print.cpp +++ b/src/ui/dialog/print.cpp @@ -81,7 +81,7 @@ static void draw_page( width, height, (unsigned long)(Inkscape::Util::Quantity::convert(width, "px", "in") * dpi), (unsigned long)(Inkscape::Util::Quantity::convert(height, "px", "in") * dpi), - dpi, dpi, bgcolor, NULL, NULL, true, NULL); + dpi, dpi, bgcolor, NULL, NULL, true, std::vector<SPItem*>()); // This doesn't seem to work: //context->set_cairo_context ( Cairo::Context::create (Cairo::ImageSurface::create_from_png (tmp_png) ), dpi, dpi ); diff --git a/src/ui/dialog/spellcheck.h b/src/ui/dialog/spellcheck.h index 27b89e34e..e98a9d80e 100644 --- a/src/ui/dialog/spellcheck.h +++ b/src/ui/dialog/spellcheck.h @@ -16,10 +16,6 @@ # include <config.h> #endif -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include <gtkmm/box.h> #include <gtkmm/button.h> #include <gtkmm/buttonbox.h> diff --git a/src/ui/dialog/svg-fonts-dialog.cpp b/src/ui/dialog/svg-fonts-dialog.cpp index bc228633d..12b423602 100644 --- a/src/ui/dialog/svg-fonts-dialog.cpp +++ b/src/ui/dialog/svg-fonts-dialog.cpp @@ -524,7 +524,7 @@ void SvgFontsDialog::set_glyph_description_from_selected_path(){ return; } - Inkscape::XML::Node* node = (Inkscape::XML::Node*) g_slist_nth_data((GSList *)sel->reprList(), 0); + Inkscape::XML::Node* node = sel->reprList().front(); if (!node) return;//TODO: should this be an assert? if (!node->matchAttributeName("d") || !node->attribute("d")){ char *msg = _("The selected object does not have a <b>path</b> description."); @@ -566,7 +566,7 @@ void SvgFontsDialog::missing_glyph_description_from_selected_path(){ return; } - Inkscape::XML::Node* node = (Inkscape::XML::Node*) g_slist_nth_data((GSList *)sel->reprList(), 0); + Inkscape::XML::Node* node = sel->reprList().front(); if (!node) return;//TODO: should this be an assert? if (!node->matchAttributeName("d") || !node->attribute("d")){ char *msg = _("The selected object does not have a <b>path</b> description."); diff --git a/src/ui/dialog/swatches.cpp b/src/ui/dialog/swatches.cpp index 8759039c3..72677c07e 100644 --- a/src/ui/dialog/swatches.cpp +++ b/src/ui/dialog/swatches.cpp @@ -123,10 +123,10 @@ static void editGradientImpl( SPDesktop* desktop, SPGradient* gr ) bool shown = false; if ( desktop && desktop->doc() ) { Inkscape::Selection *selection = desktop->getSelection(); - GSList const *items = selection->itemList(); - if (items) { + std::vector<SPItem*> const items = selection->itemList(); + if (!items.empty()) { SPStyle query( desktop->doc() ); - int result = objects_query_fillstroke(const_cast<GSList *>(items), &query, true); + int result = objects_query_fillstroke((items), &query, true); if ( (result == QUERY_STYLE_MULTIPLE_SAME) || (result == QUERY_STYLE_SINGLE) ) { // could be pertinent if (query.fill.isPaintserver()) { diff --git a/src/ui/dialog/symbols.cpp b/src/ui/dialog/symbols.cpp index eeb4d6cbe..06c17611f 100644 --- a/src/ui/dialog/symbols.cpp +++ b/src/ui/dialog/symbols.cpp @@ -18,10 +18,6 @@ #include <functional> #include <sstream> -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include <gtkmm/buttonbox.h> #include <gtkmm/label.h> diff --git a/src/ui/dialog/tags.cpp b/src/ui/dialog/tags.cpp index 7d3edaffb..f36e3f18d 100644 --- a/src/ui/dialog/tags.cpp +++ b/src/ui/dialog/tags.cpp @@ -51,7 +51,7 @@ #include "ui/tools/tool-base.h" //"event-context.h" #include "selection.h" //#include "dialogs/dialog-events.h" -#include "widgets/sp-color-notebook.h" +#include "ui/widget/color-notebook.h" #include "style.h" #include "filter-chemistry.h" #include "filters/blend.h" @@ -352,9 +352,10 @@ void TagsPanel::_objectsSelected( Selection *sel ) { _selectedConnection.block(); _tree.get_selection()->unselect_all(); - for (const GSList * iter = sel->list(); iter != NULL; iter = iter->next) + std::vector<SPObject*> tmp=sel->list(); + for(std::vector<SPObject*>::const_iterator i=tmp.begin();i!=tmp.end();i++) { - SPObject *obj = reinterpret_cast<SPObject *>(iter->data); + SPObject *obj = *i; _store->foreach(sigc::bind<SPObject *>( sigc::mem_fun(*this, &TagsPanel::_checkForSelected), obj)); } _selectedConnection.unblock(); @@ -649,9 +650,9 @@ bool TagsPanel::_handleButtonEvent(GdkEventButton* event) if (col == _tree.get_column(COL_ADD - 1) && down_at_add) { if (SP_IS_TAG(obj)) { bool wasadded = false; - for (const GSList * iter = _desktop->selection->itemList(); iter != NULL; iter = iter->next) - { - SPObject *newobj = reinterpret_cast<SPObject *>(iter->data); + std::vector<SPItem*> items=_desktop->selection->itemList(); + for(std::vector<SPItem*>::const_iterator i=items.begin();i!=items.end();i++){ + SPObject *newobj = *i; bool addchild = true; for ( SPObject *child = obj->children; child != NULL; child = child->next) { if (SP_IS_TAG_USE(child) && SP_TAG_USE(child)->ref->getObject() == newobj) { diff --git a/src/ui/dialog/text-edit.cpp b/src/ui/dialog/text-edit.cpp index a8be8b543..7575cc854 100644 --- a/src/ui/dialog/text-edit.cpp +++ b/src/ui/dialog/text-edit.cpp @@ -69,6 +69,7 @@ TextEdit::TextEdit() font_label(_("_Font"), true), layout_frame(), text_label(_("_Text"), true), + vari_label(_("_Variants"), true), setasdefault_button(_("Set as _default")), close_button(Gtk::Stock::CLOSE), apply_button(Gtk::Stock::APPLY), @@ -195,7 +196,8 @@ TextEdit::TextEdit() notebook.append_page(font_vbox, font_label); notebook.append_page(text_vbox, text_label); - + notebook.append_page(vari_vbox, vari_label); + /* Buttons */ setasdefault_button.set_use_underline(true); apply_button.set_can_default(); @@ -216,6 +218,7 @@ TextEdit::TextEdit() setasdefault_button.signal_clicked().connect(sigc::mem_fun(*this, &TextEdit::onSetDefault)); apply_button.signal_clicked().connect(sigc::mem_fun(*this, &TextEdit::onApply)); close_button.signal_clicked().connect(sigc::bind(_signal_response.make_slot(), GTK_RESPONSE_CLOSE)); + fontVariantChangedConn = vari_vbox.connectChanged(sigc::bind(sigc::ptr_fun(&onFontVariantChange), this)); desktopChangeConn = deskTrack.connectDesktopChanged( sigc::mem_fun(*this, &TextEdit::setTargetDesktop) ); deskTrack.connect(GTK_WIDGET(gobj())); @@ -230,6 +233,7 @@ TextEdit::~TextEdit() selectChangedConn.disconnect(); desktopChangeConn.disconnect(); deskTrack.disconnect(); + fontVariantChangedConn.disconnect(); } void TextEdit::styleButton(Gtk::RadioButton *button, gchar const *tooltip, gchar const *icon_name, Gtk::RadioButton *group_button ) @@ -384,6 +388,13 @@ void TextEdit::onReadSelection ( gboolean dostyle, gboolean /*docontent*/ ) gtk_entry_set_text ((GtkEntry *) gtk_bin_get_child ((GtkBin *) spacing_combo), sstr); g_free(sstr); + // Update font variant widget + //int result_variants = + sp_desktop_query_style (SP_ACTIVE_DESKTOP, &query, QUERY_STYLE_PROPERTY_FONTVARIANTS); + int result_features = + sp_desktop_query_style (SP_ACTIVE_DESKTOP, &query, QUERY_STYLE_PROPERTY_FONTFEATURESETTINGS); + vari_vbox.update( &query, result_features == QUERY_STYLE_MULTIPLE_DIFFERENT, fontspec ); + } blocked = false; } @@ -418,12 +429,11 @@ SPItem *TextEdit::getSelectedTextItem (void) if (!SP_ACTIVE_DESKTOP) return NULL; - for (const GSList *item = SP_ACTIVE_DESKTOP->getSelection()->itemList(); - item != NULL; - item = item->next) + std::vector<SPItem*> tmp=SP_ACTIVE_DESKTOP->getSelection()->itemList(); + for(std::vector<SPItem*>::const_iterator i=tmp.begin();i!=tmp.end();i++) { - if (SP_IS_TEXT(item->data) || SP_IS_FLOWTEXT(item->data)) - return SP_ITEM (item->data); + if (SP_IS_TEXT(*i) || SP_IS_FLOWTEXT(*i)) + return *i; } return NULL; @@ -437,11 +447,10 @@ unsigned TextEdit::getSelectedTextCount (void) unsigned int items = 0; - for (const GSList *item = SP_ACTIVE_DESKTOP->getSelection()->itemList(); - item != NULL; - item = item->next) + std::vector<SPItem*> tmp=SP_ACTIVE_DESKTOP->getSelection()->itemList(); + for(std::vector<SPItem*>::const_iterator i=tmp.begin();i!=tmp.end();i++) { - if (SP_IS_TEXT(item->data) || SP_IS_FLOWTEXT(item->data)) + if (SP_IS_TEXT(*i) || SP_IS_FLOWTEXT(*i)) ++items; } @@ -512,12 +521,15 @@ SPCSSAttr *TextEdit::fillTextStyle () sp_repr_css_set_property (css, "writing-mode", "tb"); } - // Note that CSS 1.1 does not support line-height; we set it for consistency, but also set + // Note that SVG 1.1 does not support line-height; we set it for consistency, but also set // sodipodi:linespacing for backwards compatibility; in 1.2 we use line-height for flowtext const gchar *sstr = gtk_combo_box_text_get_active_text ((GtkComboBoxText *) spacing_combo); sp_repr_css_set_property (css, "line-height", sstr); + // Font variants + vari_vbox.fill_css( css ); + return css; } @@ -542,20 +554,20 @@ void TextEdit::onApply() SPDesktop *desktop = SP_ACTIVE_DESKTOP; unsigned items = 0; - const GSList *item_list = desktop->getSelection()->itemList(); + const std::vector<SPItem*> item_list = desktop->getSelection()->itemList(); SPCSSAttr *css = fillTextStyle (); sp_desktop_set_style(desktop, css, true); - for (; item_list != NULL; item_list = item_list->next) { + for(std::vector<SPItem*>::const_iterator i=item_list.begin();i!=item_list.end();i++){ // apply style to the reprs of all text objects in the selection - if (SP_IS_TEXT (item_list->data)) { + if (SP_IS_TEXT (*i)) { // backwards compatibility: - reinterpret_cast<SPObject*>(item_list->data)->getRepr()->setAttribute("sodipodi:linespacing", sp_repr_css_property (css, "line-height", NULL)); + (*i)->getRepr()->setAttribute("sodipodi:linespacing", sp_repr_css_property (css, "line-height", NULL)); ++items; } - else if (SP_IS_FLOWTEXT (item_list->data)) + else if (SP_IS_FLOWTEXT (*i)) // no need to set sodipodi:linespacing, because Inkscape never supported it on flowtext ++items; } @@ -648,6 +660,19 @@ void TextEdit::onFontChange(SPFontSelector * /*fontsel*/, gchar* fontspec, TextE } +void TextEdit::onFontVariantChange(TextEdit *self) +{ + if( self->blocked ) + return; + + SPItem *text = self->getSelectedTextItem (); + + if (text) { + self->apply_button.set_sensitive ( true ); + } + self->setasdefault_button.set_sensitive ( true ); +} + void TextEdit::onStartOffsetChange(GtkTextBuffer * /*text_buffer*/, TextEdit *self) { SPItem *text = self->getSelectedTextItem(); diff --git a/src/ui/dialog/text-edit.h b/src/ui/dialog/text-edit.h index 8683d80a3..cfe612268 100644 --- a/src/ui/dialog/text-edit.h +++ b/src/ui/dialog/text-edit.h @@ -22,10 +22,6 @@ # include <config.h> #endif -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include <gtkmm/box.h> #include <gtkmm/notebook.h> #include <gtkmm/button.h> @@ -36,9 +32,11 @@ #include "ui/widget/panel.h" #include "ui/widget/frame.h" #include "ui/dialog/desktop-tracker.h" +#include "ui/widget/font-variants.h" class SPItem; struct SPFontSelector; +//class FontVariants; class font_instance; class SPCSSAttr; @@ -115,6 +113,17 @@ protected: static void onFontChange (SPFontSelector *fontsel, gchar* fontspec, TextEdit *self); /** + * Callback invoked when the user modifies the font variant through the dialog. + * + * onFontChange updates the dialog UI. The subfunction setPreviewText updates the preview label. + * + * @param fontsel pointer to FontVariant (currently not used). + * @param fontspec for the text to be previewed. + * @param self pointer to the current instance of the dialog. + */ + static void onFontVariantChange (TextEdit *self); + + /** * Callback invoked when the user modifies the startOffset of text on a path. * * @param text_buffer pointer to the GtkTextBuffer with the text of the selected text object. @@ -217,6 +226,9 @@ private: GtkWidget *text_view; // TODO - Convert this to a Gtk::TextView, but GtkSpell doesn't seem to work with it GtkTextBuffer *text_buffer; + Inkscape::UI::Widget::FontVariants vari_vbox; + Gtk::Label vari_label; + Gtk::HBox button_row; Gtk::Button setasdefault_button; Gtk::Button close_button; @@ -228,6 +240,7 @@ private: sigc::connection selectChangedConn; sigc::connection subselChangedConn; sigc::connection selectModifiedConn; + sigc::connection fontVariantChangedConn; bool blocked; const Glib::ustring samplephrase; diff --git a/src/ui/dialog/tile.h b/src/ui/dialog/tile.h index 2f75a8922..de1d3028b 100644 --- a/src/ui/dialog/tile.h +++ b/src/ui/dialog/tile.h @@ -20,10 +20,6 @@ # include <config.h> #endif -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include <gtkmm/box.h> #include <gtkmm/notebook.h> #include <gtkmm/checkbutton.h> diff --git a/src/ui/dialog/transformation.cpp b/src/ui/dialog/transformation.cpp index 233d99750..498ad7822 100644 --- a/src/ui/dialog/transformation.cpp +++ b/src/ui/dialog/transformation.cpp @@ -15,10 +15,6 @@ # include <config.h> #endif -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include <gtkmm/dialog.h> #include <gtkmm/stock.h> #include <2geom/transforms.h> @@ -655,7 +651,7 @@ void Transformation::updatePageTransform(Inkscape::Selection *selection) { if (selection && !selection->isEmpty()) { if (_check_replace_matrix.get_active()) { - Geom::Affine current (SP_ITEM(selection->itemList()->data)->transform); // take from the first item in selection + Geom::Affine current (selection->itemList()[0]->transform); // take from the first item in selection Geom::Affine new_displayed = current; @@ -740,20 +736,19 @@ void Transformation::applyPageMove(Inkscape::Selection *selection) if (_check_move_relative.get_active()) { // shift each object relatively to the previous one - using Inkscape::Util::GSListConstIterator; - std::list<SPItem *> selected; - selected.insert<GSListConstIterator<SPItem *> >(selected.end(), selection->itemList(), NULL); + std::vector<SPItem*> selected(selection->itemList()); if (selected.empty()) return; if (fabs(x) > 1e-6) { std::vector< BBoxSort > sorted; - for (std::list<SPItem *>::iterator it(selected.begin()); + for (std::vector<SPItem*>::iterator it(selected.begin()); it != selected.end(); ++it) { - Geom::OptRect bbox = (*it)->desktopPreferredBounds(); + SPItem* item = *it; + Geom::OptRect bbox = item->desktopPreferredBounds(); if (bbox) { - sorted.push_back(BBoxSort(*it, *bbox, Geom::X, x > 0? 1. : 0., x > 0? 0. : 1.)); + sorted.push_back(BBoxSort(item, *bbox, Geom::X, x > 0? 1. : 0., x > 0? 0. : 1.)); } } //sort bbox by anchors @@ -771,13 +766,14 @@ void Transformation::applyPageMove(Inkscape::Selection *selection) } if (fabs(y) > 1e-6) { std::vector< BBoxSort > sorted; - for (std::list<SPItem *>::iterator it(selected.begin()); + for (std::vector<SPItem*>::iterator it(selected.begin()); it != selected.end(); ++it) { - Geom::OptRect bbox = (*it)->desktopPreferredBounds(); + SPItem* item = *it; + Geom::OptRect bbox = item->desktopPreferredBounds(); if (bbox) { - sorted.push_back(BBoxSort(*it, *bbox, Geom::Y, y > 0? 1. : 0., y > 0? 0. : 1.)); + sorted.push_back(BBoxSort(item, *bbox, Geom::Y, y > 0? 1. : 0., y > 0? 0. : 1.)); } } //sort bbox by anchors @@ -815,8 +811,9 @@ void Transformation::applyPageScale(Inkscape::Selection *selection) bool transform_stroke = prefs->getBool("/options/transform/stroke", true); bool preserve = prefs->getBool("/options/preservetransform/value", false); if (prefs->getBool("/dialogs/transformation/applyseparately")) { - for (GSList const *l = selection->itemList(); l != NULL; l = l->next) { - SPItem *item = SP_ITEM(l->data); + std::vector<SPItem*> tmp=selection->itemList(); + for(std::vector<SPItem*>::const_iterator i=tmp.begin();i!=tmp.end();i++){ + SPItem *item = *i; Geom::OptRect bbox_pref = item->desktopPreferredBounds(); Geom::OptRect bbox_geom = item->desktopGeometricBounds(); if (bbox_pref && bbox_geom) { @@ -878,8 +875,9 @@ void Transformation::applyPageRotate(Inkscape::Selection *selection) } if (prefs->getBool("/dialogs/transformation/applyseparately")) { - for (GSList const *l = selection->itemList(); l != NULL; l = l->next) { - SPItem *item = SP_ITEM(l->data); + std::vector<SPItem*> tmp=selection->itemList(); + for(std::vector<SPItem*>::const_iterator i=tmp.begin();i!=tmp.end();i++){ + SPItem *item = *i; sp_item_rotate_rel(item, Geom::Rotate (angle*M_PI/180.0)); } } else { @@ -897,8 +895,9 @@ void Transformation::applyPageSkew(Inkscape::Selection *selection) { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); if (prefs->getBool("/dialogs/transformation/applyseparately")) { - for (GSList const *l = selection->itemList(); l != NULL; l = l->next) { - SPItem *item = SP_ITEM(l->data); + std::vector<SPItem*> items=selection->itemList(); + for(std::vector<SPItem*>::const_iterator i = items.begin();i!=items.end();i++){ + SPItem *item = *i; if (!_units_skew.isAbsolute()) { // percentage double skewX = _scalar_skew_horizontal.getValue("%"); @@ -998,10 +997,11 @@ void Transformation::applyPageTransform(Inkscape::Selection *selection) } if (_check_replace_matrix.get_active()) { - for (GSList const *l = selection->itemList(); l != NULL; l = l->next) { - SPItem *item = SP_ITEM(l->data); + std::vector<SPItem*> tmp=selection->itemList(); + for(std::vector<SPItem*>::const_iterator i=tmp.begin();i!=tmp.end();i++){ + SPItem *item = *i; item->set_item_transform(displayed); - SP_OBJECT(item)->updateRepr(); + item->updateRepr(); } } else { sp_selection_apply_affine(selection, displayed); // post-multiply each object's transform @@ -1150,7 +1150,7 @@ void Transformation::onReplaceMatrixToggled() double f = _scalar_transform_f.getValue(); Geom::Affine displayed (a, b, c, d, e, f); - Geom::Affine current = SP_ITEM(selection->itemList()->data)->transform; // take from the first item in selection + Geom::Affine current = selection->itemList()[0]->transform; // take from the first item in selection Geom::Affine new_displayed; if (_check_replace_matrix.get_active()) { diff --git a/src/ui/interface.cpp b/src/ui/interface.cpp index 87438ac01..a129d4b92 100644 --- a/src/ui/interface.cpp +++ b/src/ui/interface.cpp @@ -439,7 +439,7 @@ sp_ui_dialog_title_string(Inkscape::Verb *verb, gchar *c) gchar* key = sp_shortcut_get_label(shortcut); s = g_stpcpy(s, " ("); s = g_stpcpy(s, key); - s = g_stpcpy(s, ")"); + g_stpcpy(s, ")"); g_free(key); } } @@ -1906,11 +1906,10 @@ void ContextMenu::ActivateGroup(void) void ContextMenu::ActivateUngroup(void) { - GSList *children = NULL; + std::vector<SPItem*> children; - sp_item_group_ungroup(static_cast<SPGroup*>(_item), &children); + sp_item_group_ungroup(static_cast<SPGroup*>(_item), children); _desktop->selection->setList(children); - g_slist_free(children); } void ContextMenu::MakeAnchorMenu(void) @@ -1959,10 +1958,9 @@ void ContextMenu::AnchorLinkFollow(void) void ContextMenu::AnchorLinkRemove(void) { - GSList *children = NULL; - sp_item_group_ungroup(static_cast<SPAnchor*>(_item), &children, false); + std::vector<SPItem*> children; + sp_item_group_ungroup(static_cast<SPAnchor*>(_item), children, false); DocumentUndo::done(_desktop->doc(), SP_VERB_NONE, _("Remove link")); - g_slist_free(children); } void ContextMenu::MakeImageMenu (void) @@ -2051,8 +2049,6 @@ void ContextMenu::ImageEdit(void) _desktop->selection->set(_item); } - GSList const *selected = _desktop->selection->itemList(); - GError* errThing = 0; Glib::ustring cmdline = getImageEditorName(); Glib::ustring name; @@ -2079,8 +2075,9 @@ void ContextMenu::ImageEdit(void) } #endif - for (GSList const *iter = selected; iter != NULL; iter = iter->next) { - Inkscape::XML::Node *ir = SP_ITEM(iter->data)->getRepr(); + std::vector<SPItem*> itemlist=_desktop->selection->itemList(); + for(std::vector<SPItem*>::const_iterator i=itemlist.begin();i!=itemlist.end();i++){ + Inkscape::XML::Node *ir = (*i)->getRepr(); const gchar *href = ir->attribute("xlink:href"); if (strncmp (href,"file:",5) == 0) { diff --git a/src/ui/previewholder.h b/src/ui/previewholder.h index f9f923be4..f6d1985cc 100644 --- a/src/ui/previewholder.h +++ b/src/ui/previewholder.h @@ -16,10 +16,6 @@ # include "config.h" #endif -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include <gtkmm/box.h> #include <gtkmm/bin.h> diff --git a/src/ui/selected-color.cpp b/src/ui/selected-color.cpp new file mode 100644 index 000000000..8c37ee7e0 --- /dev/null +++ b/src/ui/selected-color.cpp @@ -0,0 +1,163 @@ +/** @file + * Color selected in color selector widget. + * This file was created during the refactoring of SPColorSelector + *//* + * Authors: + * bulia byak <buliabyak@users.sf.net> + * Jon A. Cruz <jon@joncruz.org> + * Tomasz Boczkowski <penginsbacon@gmail.com> + * + * Copyright (C) 2014 Authors + * Released under GNU GPL, read the file 'COPYING' for more information + */ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <glibmm/ustring.h> +#include <cmath> + +#include "svg/svg-icc-color.h" +#include "ui/selected-color.h" + +namespace Inkscape { +namespace UI { + +double const SelectedColor::_EPSILON = 1e-4; + +SelectedColor::SelectedColor() + : _color(0) + , _alpha(1.0) + , _held(false) + , _virgin(true) + , _updating(false) +{ + +} + +SelectedColor::~SelectedColor() { + +} + +void SelectedColor::setColor(SPColor const &color) +{ + setColorAlpha( color, _alpha); +} + +SPColor SelectedColor::color() const +{ + return _color; +} + +void SelectedColor::setAlpha(gfloat alpha) +{ + g_return_if_fail( ( 0.0 <= alpha ) && ( alpha <= 1.0 ) ); + setColorAlpha( _color, alpha); +} + +gfloat SelectedColor::alpha() const +{ + return _alpha; +} + +void SelectedColor::setValue(guint32 value) +{ + SPColor color(value); + gfloat alpha = SP_RGBA32_A_F(value); + setColorAlpha(color, alpha); +} + +guint32 SelectedColor::value() const +{ + return color().toRGBA32(_alpha); +} + +void SelectedColor::setColorAlpha(SPColor const &color, gfloat alpha, bool emit_signal) +{ +#ifdef DUMP_CHANGE_INFO + g_message("SelectedColor::setColorAlpha( this=%p, %f, %f, %f, %s, %f, %s)", this, color.v.c[0], color.v.c[1], color.v.c[2], (color.icc?color.icc->colorProfile.c_str():"<null>"), alpha, (emit_signal?"YES":"no")); +#endif + g_return_if_fail( ( 0.0 <= alpha ) && ( alpha <= 1.0 ) ); + + if (_updating) { + return; + } + +#ifdef DUMP_CHANGE_INFO + g_message("---- SelectedColor::setColorAlpha virgin:%s !close:%s alpha is:%s", + (_virgin?"YES":"no"), + (!color.isClose( _color, _EPSILON )?"YES":"no"), + ((fabs((_alpha) - (alpha)) >= _EPSILON )?"YES":"no") + ); +#endif + + if ( _virgin || !color.isClose( _color, _EPSILON ) || + (fabs((_alpha) - (alpha)) >= _EPSILON )) { + + _virgin = false; + + _color = color; + _alpha = alpha; + + if (emit_signal) + { + _updating = true; + if (_held) { + signal_dragged.emit(); + } else { + signal_changed.emit(); + } + _updating = false; + } + +#ifdef DUMP_CHANGE_INFO + } else { + g_message("++++ SelectedColor::setColorAlpha color:%08x ==> _color:%08X isClose:%s", color.toRGBA32(alpha), _color.toRGBA32(_alpha), + (color.isClose( _color, _EPSILON )?"YES":"no")); +#endif + } +} + +void SelectedColor::colorAlpha(SPColor &color, gfloat &alpha) const { + color = _color; + alpha = _alpha; +} + +void SelectedColor::setHeld(bool held) { + if (_updating) { + return; + } + bool grabbed = held && !_held; + bool released = !held && _held; + + _held = held; + + _updating = true; + if (grabbed) { + signal_grabbed.emit(); + } + + if (released) { + signal_released.emit(); + signal_changed.emit(); + } + _updating = false; +} + +void SelectedColor::preserveICC() { + _color.icc = _color.icc ? new SVGICCColor(*_color.icc) : 0; +} + +} +} + +/* + 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/ui/selected-color.h b/src/ui/selected-color.h new file mode 100644 index 000000000..e9e702d43 --- /dev/null +++ b/src/ui/selected-color.h @@ -0,0 +1,96 @@ +/** @file + * Color selected in color selector widget. + * This file was created during the refactoring of SPColorSelector + *//* + * Authors: + * bulia byak <buliabyak@users.sf.net> + * Jon A. Cruz <jon@joncruz.org> + * Tomasz Boczkowski <penginsbacon@gmail.com> + * + * Copyright (C) 2014 Authors + * Released under GNU GPL, read the file 'COPYING' for more information + */ +#ifndef SEEN_SELECTED_COLOR +#define SEEN_SELECTED_COLOR + +#include <sigc++/signal.h> +#include "color.h" + +namespace Gtk +{ + class Widget; +} + +namespace Inkscape { +namespace UI { + +class SelectedColor { +public: + SelectedColor(); + virtual ~SelectedColor(); + + void setColor(SPColor const &color); + SPColor color() const; + + void setAlpha(gfloat alpha); + gfloat alpha() const; + + void setValue(guint32 value); + guint32 value() const; + + void setColorAlpha(SPColor const &color, gfloat alpha, bool emit_signal = true); + void colorAlpha(SPColor &color, gfloat &alpha) const; + + void setHeld(bool held); + + void preserveICC(); + + sigc::signal<void> signal_grabbed; + sigc::signal<void> signal_dragged; + sigc::signal<void> signal_released; + sigc::signal<void> signal_changed; +private: + // By default, disallow copy constructor and assignment operator + SelectedColor(SelectedColor const &obj); + SelectedColor& operator=(SelectedColor const &obj); + + SPColor _color; + /** + * Color alpha value guaranteed to be in [0, 1]. + */ + gfloat _alpha; + + bool _held; + /** + * This flag is true if no color is set yet + */ + bool _virgin; + + bool _updating; + + static double const _EPSILON; +}; + +class ColorSelectorFactory { +public: + virtual ~ColorSelectorFactory() { + } + + virtual Gtk::Widget* createWidget(SelectedColor &color) const = 0; + virtual Glib::ustring modeName() const = 0; +}; + +} +} + +#endif +/* + 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/ui/tool/control-point.cpp b/src/ui/tool/control-point.cpp index bcf5c9fce..636595016 100644 --- a/src/ui/tool/control-point.cpp +++ b/src/ui/tool/control-point.cpp @@ -71,7 +71,8 @@ ControlPoint::ControlPoint(SPDesktop *d, Geom::Point const &initial_pos, SPAncho _cset(cset), _state(STATE_NORMAL), _position(initial_pos), - _lurking(false) + _lurking(false), + _double_clicked(false) { _canvas_item = sp_canvas_item_new( group ? group : _desktop->getControls(), SP_TYPE_CTRL, @@ -80,6 +81,7 @@ ControlPoint::ControlPoint(SPDesktop *d, Geom::Point const &initial_pos, SPAncho "filled", TRUE, "fill_color", _cset.normal.fill, "stroked", TRUE, "stroke_color", _cset.normal.stroke, "mode", SP_CTRL_MODE_XOR, NULL); + _commonInit(); } @@ -91,7 +93,8 @@ ControlPoint::ControlPoint(SPDesktop *d, Geom::Point const &initial_pos, SPAncho _cset(cset), _state(STATE_NORMAL), _position(initial_pos), - _lurking(false) + _lurking(false), + _double_clicked(false) { _canvas_item = ControlManager::getManager().createControl(group ? group : _desktop->getControls(), type); g_object_set(_canvas_item, @@ -245,7 +248,8 @@ bool ControlPoint::_eventHandler(Inkscape::UI::Tools::ToolBase *event_context, G static Geom::Point pointer_offset; // number of last doubleclicked button static unsigned next_release_doubleclick = 0; - + _double_clicked = false; + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); int drag_tolerance = prefs->getIntLimited("/options/dragtolerance/value", 0, 0, 100); GdkEventMotion em; @@ -278,6 +282,7 @@ bool ControlPoint::_eventHandler(Inkscape::UI::Tools::ToolBase *event_context, G Ca = _desktop->canvas; em = event->motion; combine_motion_events(Ca, em, 0); + if (_event_grab && ! event_context->space_panning) { _desktop->snapindicator->remove_snaptarget(); bool transferred = false; @@ -298,6 +303,7 @@ bool ControlPoint::_eventHandler(Inkscape::UI::Tools::ToolBase *event_context, G _drag_initiated = true; } } + if (!transferred) { // dragging in progress Geom::Point new_pos = _desktop->w2d(event_point(event->motion)) + pointer_offset; @@ -305,7 +311,7 @@ bool ControlPoint::_eventHandler(Inkscape::UI::Tools::ToolBase *event_context, G dragged(new_pos, &em); move(new_pos); _updateDragTip(&em); // update dragging tip after moving to new position - + _desktop->scroll_to_point(new_pos); _desktop->set_coordinate_status(_position); sp_event_context_snap_delay_handler(event_context, NULL, @@ -342,6 +348,7 @@ bool ControlPoint::_eventHandler(Inkscape::UI::Tools::ToolBase *event_context, G } else { // it is the end of a click if (next_release_doubleclick) { + _double_clicked = true; return doubleclicked(&event->button); } else { return clicked(&event->button); diff --git a/src/ui/tool/control-point.h b/src/ui/tool/control-point.h index b3ed9545e..4a01b9f21 100644 --- a/src/ui/tool/control-point.h +++ b/src/ui/tool/control-point.h @@ -193,6 +193,8 @@ public: virtual bool _eventHandler(Inkscape::UI::Tools::ToolBase *event_context, GdkEvent *event); SPDesktop *const _desktop; ///< The desktop this control point resides on. + bool doubleClicked() {return _double_clicked;} + protected: struct ColorEntry { @@ -361,6 +363,8 @@ protected: /** Events which should be captured when a handle is being dragged. */ static int const _grab_event_mask; + static bool _drag_initiated; + private: ControlPoint(ControlPoint const &other); @@ -397,7 +401,7 @@ private: static bool _event_grab; - static bool _drag_initiated; + bool _double_clicked; }; diff --git a/src/ui/tool/curve-drag-point.cpp b/src/ui/tool/curve-drag-point.cpp index 23640456e..e460b0fb7 100644 --- a/src/ui/tool/curve-drag-point.cpp +++ b/src/ui/tool/curve-drag-point.cpp @@ -15,6 +15,8 @@ #include "ui/tool/multi-path-manipulator.h" #include "ui/tool/path-manipulator.h" #include "ui/tool/node.h" +#include "sp-namedview.h" +#include "snap.h" namespace Inkscape { namespace UI { @@ -77,6 +79,16 @@ void CurveDragPoint::dragged(Geom::Point &new_pos, GdkEventMotion *event) return; } + if (_drag_initiated && !(event->state & GDK_SHIFT_MASK)) { + SnapManager &m = _desktop->namedview->snap_manager; + SPItem *path = static_cast<SPItem *>(_pm._path); + m.setup(_desktop, true, path); // We will not try to snap to "path" itself + Inkscape::SnapCandidatePoint scp(new_pos, Inkscape::SNAPSOURCE_OTHER_HANDLE); + Inkscape::SnappedPoint sp = m.freeSnap(scp, Geom::OptRect(), false); + new_pos = sp.getPoint(); + m.unSetup(); + } + // Magic Bezier Drag Equations follow! // "weight" describes how the influence of the drag should be distributed // among the handles; 0 = front handle only, 1 = back handle only. @@ -166,14 +178,8 @@ void CurveDragPoint::_insertNode(bool take_selection) // Otherwise clicks on the new node would only work after the user moves the mouse a bit. // PathManipulator will restore visibility when necessary. setVisible(false); - NodeList::iterator inserted = _pm.subdivideSegment(first, _t); - if (take_selection) { - _pm._selection.clear(); - } - _pm._selection.insert(inserted.ptr()); - _pm.update(true); - _pm._commit(_("Add node")); + _pm.insertNode(first, _t, take_selection); } Glib::ustring CurveDragPoint::_getTip(unsigned state) const @@ -181,6 +187,10 @@ Glib::ustring CurveDragPoint::_getTip(unsigned state) const if (_pm.empty()) return ""; if (!first || !first.next()) return ""; bool linear = first->front()->isDegenerate() && first.next()->back()->isDegenerate(); + if(state_held_shift(state) && _pm._isBSpline()){ + return C_("Path segment tip", + "<b>Shift</b>: drag to open or move BSpline handles"); + } if (state_held_shift(state)) { return C_("Path segment tip", "<b>Shift</b>: click to toggle segment selection"); @@ -189,6 +199,11 @@ Glib::ustring CurveDragPoint::_getTip(unsigned state) const return C_("Path segment tip", "<b>Ctrl+Alt</b>: click to insert a node"); } + if(_pm._isBSpline()){ + return C_("Path segment tip", + "<b>BSpline segment</b>: drag to shape the segment, doubleclick to insert node, " + "click to select (more: Shift, Ctrl+Alt)"); + } if (linear) { return C_("Path segment tip", "<b>Linear segment</b>: drag to convert to a Bezier segment, " diff --git a/src/ui/tool/curve-drag-point.h b/src/ui/tool/curve-drag-point.h index ea83978e0..c1d40575f 100644 --- a/src/ui/tool/curve-drag-point.h +++ b/src/ui/tool/curve-drag-point.h @@ -34,7 +34,9 @@ public: CurveDragPoint(PathManipulator &pm); void setSize(double sz) { _setSize(sz); } void setTimeValue(double t) { _t = t; } + double getTimeValue() { return _t; } void setIterator(NodeList::iterator i) { first = i; } + NodeList::iterator getIterator() { return first; } virtual bool _eventHandler(Inkscape::UI::Tools::ToolBase *event_context, GdkEvent *event); protected: @@ -47,9 +49,6 @@ protected: virtual bool doubleclicked(GdkEventButton *); private: - - void _insertNode(bool take_selection); - double _t; PathManipulator &_pm; NodeList::iterator first; @@ -57,6 +56,7 @@ private: static bool _drags_stroke; static bool _segment_was_degenerate; static Geom::Point _stroke_drag_origin; + void _insertNode(bool take_selection); }; } // namespace UI diff --git a/src/ui/tool/multi-path-manipulator.cpp b/src/ui/tool/multi-path-manipulator.cpp index f53cef5f4..46c6246a1 100644 --- a/src/ui/tool/multi-path-manipulator.cpp +++ b/src/ui/tool/multi-path-manipulator.cpp @@ -339,6 +339,14 @@ void MultiPathManipulator::insertNodesAtExtrema(ExtremumType extremum) _done(_("Add extremum nodes")); } +void MultiPathManipulator::insertNode(Geom::Point pt) +{ + // When double clicking to insert nodes, we might not have a selection of nodes (and we don't need one) + // so don't check for "_selection.empty()" here, contrary to the other methods above and below this one + invokeForAll(&PathManipulator::insertNode, pt); + _done(_("Add nodes")); +} + void MultiPathManipulator::duplicateNodes() { if (_selection.empty()) return; diff --git a/src/ui/tool/multi-path-manipulator.h b/src/ui/tool/multi-path-manipulator.h index 1bbcdd7ec..c908cede2 100644 --- a/src/ui/tool/multi-path-manipulator.h +++ b/src/ui/tool/multi-path-manipulator.h @@ -53,6 +53,7 @@ public: void insertNodesAtExtrema(ExtremumType extremum); void insertNodes(); + void insertNode(Geom::Point pt); void alertLPE(); void duplicateNodes(); void joinNodes(); diff --git a/src/ui/tool/node.cpp b/src/ui/tool/node.cpp index aa5365265..ca6f5abb1 100644 --- a/src/ui/tool/node.cpp +++ b/src/ui/tool/node.cpp @@ -356,8 +356,8 @@ void Handle::dragged(Geom::Point &new_pos, GdkEventMotion *event) Geom::Line perp_line(parent_pos, parent_pos + Geom::rot90(origin - parent_pos)); Geom::Point snap_pos = parent_pos + Geom::constrain_angle( Geom::Point(0,0), new_pos - parent_pos, snaps, Geom::Point(1,0)); - Geom::Point orig_pos = original_line.pointAt(original_line.nearestPoint(new_pos)); - Geom::Point perp_pos = perp_line.pointAt(perp_line.nearestPoint(new_pos)); + Geom::Point orig_pos = original_line.pointAt(original_line.nearestTime(new_pos)); + Geom::Point perp_pos = perp_line.pointAt(perp_line.nearestTime(new_pos)); Geom::Point result = snap_pos; ctrl_constraint = Inkscape::Snapper::SnapConstraint(parent_pos, parent_pos - snap_pos); @@ -1464,13 +1464,14 @@ Glib::ustring Node::_getTip(unsigned state) const // No modifiers: assemble tip from node type char const *nodetype = node_type_to_localized_string(_type); + double power = _pm()._bsplineHandlePosition(h,h2); if (_selection.transformHandlesEnabled() && selected()) { if (_selection.size() == 1 && !isBSpline) { return format_tip(C_("Path node tip", "<b>%s</b>: drag to shape the path (more: Shift, Ctrl, Alt)"), nodetype); }else if(_selection.size() == 1){ return format_tip(C_("Path node tip", - "<b>BSpline node</b>: drag to shape the path (more: Shift, Ctrl, Alt). %g power"),_pm()._bsplineHandlePosition(h,h2)); + "<b>BSpline node</b>: drag to shape the path (more: Shift, Ctrl, Alt). %g power"), power); } return format_tip(C_("Path node tip", "<b>%s</b>: drag to shape the path, click to toggle scale/rotation handles (more: Shift, Ctrl, Alt)"), nodetype); @@ -1480,7 +1481,7 @@ Glib::ustring Node::_getTip(unsigned state) const "<b>%s</b>: drag to shape the path, click to select only this node (more: Shift, Ctrl, Alt)"), nodetype); }else{ return format_tip(C_("Path node tip", - "<b>BSpline node</b>: drag to shape the path, click to select only this node (more: Shift, Ctrl, Alt). %g power"),_pm()._bsplineHandlePosition(h,h2)); + "<b>BSpline node</b>: drag to shape the path, click to select only this node (more: Shift, Ctrl, Alt). %g power"), power); } } @@ -1567,6 +1568,13 @@ NodeList::iterator NodeList::before(double t, double *fracpart) return ret; } +NodeList::iterator NodeList::before(Geom::PathTime const &pvp) +{ + iterator ret = begin(); + std::advance(ret, pvp.curve_index); + return ret; +} + NodeList::iterator NodeList::insert(iterator pos, Node *x) { ListNode *ins = pos._node; diff --git a/src/ui/tool/node.h b/src/ui/tool/node.h index 101af4817..025c460e2 100644 --- a/src/ui/tool/node.h +++ b/src/ui/tool/node.h @@ -406,9 +406,13 @@ public: void setClosed(bool c) { _closed = c; } iterator before(double t, double *fracpart = NULL); + iterator before(Geom::PathTime const &pvp); const_iterator before(double t, double *fracpart = NULL) const { return const_iterator(before(t, fracpart)._node); } + const_iterator before(Geom::PathTime const &pvp) const { + return const_iterator(before(pvp)._node); + } // list operations diff --git a/src/ui/tool/path-manipulator.cpp b/src/ui/tool/path-manipulator.cpp index 6b0c95f68..848b10373 100644 --- a/src/ui/tool/path-manipulator.cpp +++ b/src/ui/tool/path-manipulator.cpp @@ -174,7 +174,8 @@ bool PathManipulator::event(Inkscape::UI::Tools::ToolBase * /*event_context*/, G case GDK_MOTION_NOTIFY: _updateDragPoint(event_point(event->motion)); break; - default: break; + default: + break; } return false; } @@ -275,6 +276,27 @@ void PathManipulator::insertNodes() } } +void PathManipulator::insertNode(Geom::Point pt) +{ + Geom::Coord dist = _updateDragPoint(pt); + if (dist < 1e-5) { // 1e-6 is too small, as observed occasionally when inserting a node at a snapped intersection of paths + insertNode(_dragpoint->getIterator(), _dragpoint->getTimeValue(), true); + } +} + +void PathManipulator::insertNode(NodeList::iterator first, double t, bool take_selection) +{ + NodeList::iterator inserted = subdivideSegment(first, t); + if (take_selection) { + _selection.clear(); + } + _selection.insert(inserted.ptr()); + + update(true); + _commit(_("Add node")); +} + + static void add_or_replace_if_extremum(std::vector< std::pair<NodeList::iterator, double> > &vec, double & extrvalue, double testvalue, NodeList::iterator const& node, double t) @@ -996,7 +1018,7 @@ NodeList::iterator PathManipulator::subdivideSegment(NodeList::iterator first, d Geom::CubicBezier temp(first->position(), first->front()->position(), second->back()->position(), second->position()); std::pair<Geom::CubicBezier, Geom::CubicBezier> div = temp.subdivide(t); - std::vector<Geom::Point> seg1 = div.first.points(), seg2 = div.second.points(); + std::vector<Geom::Point> seg1 = div.first.controlPoints(), seg2 = div.second.controlPoints(); // set new handle positions Node *n = new Node(_multi_path_manipulator._path_data.node_data, seg2[0]); @@ -1141,22 +1163,22 @@ void PathManipulator::_createControlPointsFromGeometry() pathv *= (_edit_transform * _i2d_transform); // in this loop, we know that there are no zero-segment subpaths - for (Geom::PathVector::const_iterator pit = pathv.begin(); pit != pathv.end(); ++pit) { + for (Geom::PathVector::iterator pit = pathv.begin(); pit != pathv.end(); ++pit) { // prepare new subpath SubpathPtr subpath(new NodeList(_subpaths)); _subpaths.push_back(subpath); Node *previous_node = new Node(_multi_path_manipulator._path_data.node_data, pit->initialPoint()); subpath->push_back(previous_node); - Geom::Curve const &cseg = pit->back_closed(); - bool fuse_ends = pit->closed() - && Geom::are_near(cseg.initialPoint(), cseg.finalPoint()); - for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_open(); ++cit) { + + bool closed = pit->closed(); + + for (Geom::Path::iterator cit = pit->begin(); cit != pit->end(); ++cit) { Geom::Point pos = cit->finalPoint(); Node *current_node; // if the closing segment is degenerate and the path is closed, we need to move // the handle of the first node instead of creating a new one - if (fuse_ends && cit == --(pit->end_open())) { + if (closed && cit == --(pit->end())) { current_node = subpath->begin().get_pointer(); } else { /* regardless of segment type, create a new node at the end @@ -1271,7 +1293,7 @@ double PathManipulator::_bsplineHandlePosition(Handle *h, Handle *h2) line_inside_nodes->moveto(n->position()); line_inside_nodes->lineto(next_node->position()); if(!are_near(h->position(), n->position())){ - pos = Geom::nearest_point(Geom::Point(h->position()[X] - HANDLE_CUBIC_GAP, h->position()[Y] - HANDLE_CUBIC_GAP), *line_inside_nodes->first_segment()); + pos = Geom::nearest_time(Geom::Point(h->position()[X] - HANDLE_CUBIC_GAP, h->position()[Y] - HANDLE_CUBIC_GAP), *line_inside_nodes->first_segment()); } } if (pos == NO_POWER && !h2){ @@ -1425,7 +1447,7 @@ void PathManipulator::_updateOutline() Geom::PathVector arrows; for (Geom::PathVector::iterator i = pv.begin(); i != pv.end(); ++i) { Geom::Path &path = *i; - for (Geom::Path::const_iterator j = path.begin(); j != path.end_default(); ++j) { + for (Geom::Path::iterator j = path.begin(); j != path.end_default(); ++j) { Geom::Point at = j->pointAt(0.5); Geom::Point ut = j->unitTangentAt(0.5); // rotate the point @@ -1643,33 +1665,40 @@ void PathManipulator::_commit(Glib::ustring const &annotation, gchar const *key) /** Update the position of the curve drag point such that it is over the nearest * point of the path. */ -void PathManipulator::_updateDragPoint(Geom::Point const &evp) +Geom::Coord PathManipulator::_updateDragPoint(Geom::Point const &evp) { + Geom::Coord dist = HUGE_VAL; + Geom::Affine to_desktop = _edit_transform * _i2d_transform; Geom::PathVector pv = _spcurve->get_pathvector(); - boost::optional<Geom::PathVectorPosition> pvp - = Geom::nearestPoint(pv, _desktop->w2d(evp) * to_desktop.inverse()); - if (!pvp) return; - Geom::Point nearest_point = _desktop->d2w(pv.at(pvp->path_nr).pointAt(pvp->t) * to_desktop); - - double fracpart; + + boost::optional<Geom::PathVectorTime> pvp = + pv.nearestTime(_desktop->w2d(evp) * to_desktop.inverse()); + if (!pvp) return dist; + Geom::Point nearest_pt = _desktop->d2w(pv.pointAt(*pvp) * to_desktop); + + double fracpart = pvp->t; std::list<SubpathPtr>::iterator spi = _subpaths.begin(); - for (unsigned i = 0; i < pvp->path_nr; ++i, ++spi) {} - NodeList::iterator first = (*spi)->before(pvp->t, &fracpart); + for (unsigned i = 0; i < pvp->path_index; ++i, ++spi) {} + NodeList::iterator first = (*spi)->before(pvp->asPathTime()); + dist = Geom::distance(evp, nearest_pt); + double stroke_tolerance = _getStrokeTolerance(); if (first && first.next() && fracpart != 0.0 && - Geom::distance(evp, nearest_point) < stroke_tolerance) + dist < stroke_tolerance) { _dragpoint->setVisible(true); - _dragpoint->setPosition(_desktop->w2d(nearest_point)); + _dragpoint->setPosition(_desktop->w2d(nearest_pt)); _dragpoint->setSize(2 * stroke_tolerance); _dragpoint->setTimeValue(fracpart); _dragpoint->setIterator(first); } else { _dragpoint->setVisible(false); } + + return dist; } /// This is called on zoom change to update the direction arrows diff --git a/src/ui/tool/path-manipulator.h b/src/ui/tool/path-manipulator.h index 2219af849..4c6f74ba4 100644 --- a/src/ui/tool/path-manipulator.h +++ b/src/ui/tool/path-manipulator.h @@ -70,6 +70,8 @@ public: void insertNodeAtExtremum(ExtremumType extremum); void insertNodes(); + void insertNode(Geom::Point); + void insertNode(NodeList::iterator first, double t, bool take_selection); void duplicateNodes(); void weldNodes(NodeList::iterator preserve_pos = NodeList::iterator()); void weldSegments(); @@ -133,7 +135,7 @@ private: void _removeNodesFromSelection(); void _commit(Glib::ustring const &annotation); void _commit(Glib::ustring const &annotation, gchar const *key); - void _updateDragPoint(Geom::Point const &); + Geom::Coord _updateDragPoint(Geom::Point const &); void _updateOutlineOnZoomChange(); double _getStrokeTolerance(); Handle *_chooseHandle(Node *n, int which); @@ -143,7 +145,7 @@ private: SPPath *_path; ///< can be an SPPath or an Inkscape::LivePathEffect::Effect !!! SPCurve *_spcurve; // in item coordinates SPCanvasItem *_outline; - CurveDragPoint *_dragpoint; // an invisible control point hoverng over curve + CurveDragPoint *_dragpoint; // an invisible control point hovering over curve PathManipulatorObserver *_observer; Geom::Affine _d2i_transform; ///< desktop-to-item transform Geom::Affine _i2d_transform; ///< item-to-desktop transform, inverse of _d2i_transform diff --git a/src/ui/tool/selector.cpp b/src/ui/tool/selector.cpp index e4e701785..051cb41ae 100644 --- a/src/ui/tool/selector.cpp +++ b/src/ui/tool/selector.cpp @@ -129,6 +129,10 @@ bool Selector::event(Inkscape::UI::Tools::ToolBase *event_context, GdkEvent *eve return false; } +bool Selector::doubleClicked() { + return _dragger->doubleClicked(); +} + } // namespace UI } // namespace Inkscape diff --git a/src/ui/tool/selector.h b/src/ui/tool/selector.h index dbe751ede..bd8d3e1aa 100644 --- a/src/ui/tool/selector.h +++ b/src/ui/tool/selector.h @@ -29,6 +29,7 @@ public: Selector(SPDesktop *d); virtual ~Selector(); virtual bool event(Inkscape::UI::Tools::ToolBase *, GdkEvent *); + virtual bool doubleClicked(); sigc::signal<void, Geom::Rect const &, GdkEventButton*> signal_area; sigc::signal<void, Geom::Point const &, GdkEventButton*> signal_point; diff --git a/src/ui/tools/box3d-tool.cpp b/src/ui/tools/box3d-tool.cpp index 538e0c7e2..27e755add 100644 --- a/src/ui/tools/box3d-tool.cpp +++ b/src/ui/tools/box3d-tool.cpp @@ -165,7 +165,7 @@ bool Box3dTool::item_handler(SPItem* item, GdkEvent* event) { case GDK_BUTTON_PRESS: if ( event->button.button == 1 && !this->space_panning) { Inkscape::setup_for_drag_start(desktop, this, event); - ret = TRUE; + //ret = TRUE; } break; // motion and release are always on root (why?) diff --git a/src/ui/tools/calligraphic-tool.cpp b/src/ui/tools/calligraphic-tool.cpp index 15e6527a3..28195eb75 100644 --- a/src/ui/tools/calligraphic-tool.cpp +++ b/src/ui/tools/calligraphic-tool.cpp @@ -140,8 +140,7 @@ void CalligraphicTool::setup() { { /* TODO: have a look at DropperTool::setup where the same is done.. generalize? */ - Geom::PathVector path; - Geom::Circle(0, 0, 1).getPath(path); + Geom::PathVector path = Geom::Path(Geom::Circle(0,0,1)); SPCurve *c = new SPCurve(path); diff --git a/src/ui/tools/connector-tool.cpp b/src/ui/tools/connector-tool.cpp index c3ef19507..0a36877ff 100644 --- a/src/ui/tools/connector-tool.cpp +++ b/src/ui/tools/connector-tool.cpp @@ -1303,12 +1303,12 @@ void cc_selection_set_avoid(bool const set_avoid) Inkscape::Selection *selection = desktop->getSelection(); - GSList *l = const_cast<GSList *>(selection->itemList()); int changes = 0; - while (l) { - SPItem *item = SP_ITEM(l->data); + std::vector<SPItem*> l = selection->itemList(); + for(std::vector<SPItem*>::const_iterator i=l.begin();i!=l.end();i++) { + SPItem *item = *i; char const *value = (set_avoid) ? "true" : NULL; @@ -1317,8 +1317,6 @@ void cc_selection_set_avoid(bool const set_avoid) item->avoidRef->handleSettingChange(); changes++; } - - l = l->next; } if (changes == 0) { diff --git a/src/ui/tools/dropper-tool.cpp b/src/ui/tools/dropper-tool.cpp index bda9d8e8a..c838c27d5 100644 --- a/src/ui/tools/dropper-tool.cpp +++ b/src/ui/tools/dropper-tool.cpp @@ -84,8 +84,7 @@ void DropperTool::setup() { ToolBase::setup(); /* TODO: have a look at CalligraphicTool::setup where the same is done.. generalize? */ - Geom::PathVector path; - Geom::Circle(0, 0, 1).getPath(path); + Geom::PathVector path = Geom::Path(Geom::Circle(0,0,1)); SPCurve *c = new SPCurve(path); diff --git a/src/ui/tools/eraser-tool.cpp b/src/ui/tools/eraser-tool.cpp index af0cbcb78..e416fd7ef 100644 --- a/src/ui/tools/eraser-tool.cpp +++ b/src/ui/tools/eraser-tool.cpp @@ -666,8 +666,7 @@ void EraserTool::set_to_accumulated() { Geom::OptRect eraserBbox = acid->visualBounds(); Geom::Rect bounds = (*eraserBbox) * desktop->doc2dt(); std::vector<SPItem*> remainingItems; - GSList* toWorkOn = 0; - + std::vector<SPItem*> toWorkOn; if (selection->isEmpty()) { if ( eraserMode ) { toWorkOn = desktop->getDocument()->getItemsPartiallyInBox(desktop->dkey, bounds); @@ -675,17 +674,16 @@ void EraserTool::set_to_accumulated() { Inkscape::Rubberband *r = Inkscape::Rubberband::get(desktop); toWorkOn = desktop->getDocument()->getItemsAtPoints(desktop->dkey, r->getPoints()); } - - toWorkOn = g_slist_remove( toWorkOn, acid ); + toWorkOn.erase(std::remove(toWorkOn.begin(), toWorkOn.end(), acid), toWorkOn.end()); } else { - toWorkOn = g_slist_copy(const_cast<GSList*>(selection->itemList())); + toWorkOn= selection->itemList(); wasSelection = true; } - if ( g_slist_length(toWorkOn) > 0 ) { + if ( !toWorkOn.empty() ) { if ( eraserMode ) { - for (GSList *i = toWorkOn ; i ; i = i->next ) { - SPItem *item = SP_ITEM(i->data); + for (std::vector<SPItem*>::const_iterator i = toWorkOn.begin(); i != toWorkOn.end(); i++){ + SPItem *item = *i; if ( eraserMode ) { Geom::OptRect bbox = item->visualBounds(); @@ -702,13 +700,10 @@ void EraserTool::set_to_accumulated() { if ( !selection->isEmpty() ) { // If the item was not completely erased, track the new remainder. - GSList *nowSel = g_slist_copy(const_cast<GSList *>(selection->itemList())); - - for (GSList const *i2 = nowSel ; i2 ; i2 = i2->next ) { - remainingItems.push_back(SP_ITEM(i2->data)); + std::vector<SPItem*> nowSel(selection->itemList()); + for (std::vector<SPItem*>::const_iterator i2 = nowSel.begin();i2!=nowSel.end();i2++) { + remainingItems.push_back(*i2); } - - g_slist_free(nowSel); } } else { remainingItems.push_back(item); @@ -716,20 +711,18 @@ void EraserTool::set_to_accumulated() { } } } else { - for (GSList *i = toWorkOn ; i ; i = i->next ) { - sp_object_ref( SP_ITEM(i->data), 0 ); + for (std::vector<SPItem*> ::const_iterator i = toWorkOn.begin();i!=toWorkOn.end();i++) { + sp_object_ref( *i, 0 ); } - for (GSList *i = toWorkOn ; i ; i = i->next ) { - SPItem *item = SP_ITEM(i->data); + for (std::vector<SPItem*>::const_iterator i = toWorkOn.begin();i!=toWorkOn.end();i++) { + SPItem *item = *i; item->deleteObject(true); sp_object_unref(item); workDone = true; } } - g_slist_free(toWorkOn); - if ( !eraserMode ) { //sp_selection_delete(desktop); remainingItems.clear(); @@ -796,6 +789,8 @@ add_cap(SPCurve *curve, } void EraserTool::accumulate() { + // construct a crude outline of the eraser's path. + // this desperately needs to be rewritten to use the path outliner... if ( !this->cal1->is_empty() && !this->cal2->is_empty() ) { this->accumulated->reset(); /* Is this required ?? */ SPCurve *rev_cal2 = this->cal2->create_reverse(); @@ -805,10 +800,10 @@ void EraserTool::accumulate() { g_assert( ! this->cal1->first_path()->closed() ); g_assert( ! rev_cal2->first_path()->closed() ); - Geom::CubicBezier const * dc_cal1_firstseg = dynamic_cast<Geom::CubicBezier const *>( this->cal1->first_segment() ); - Geom::CubicBezier const * rev_cal2_firstseg = dynamic_cast<Geom::CubicBezier const *>( rev_cal2->first_segment() ); - Geom::CubicBezier const * dc_cal1_lastseg = dynamic_cast<Geom::CubicBezier const *>( this->cal1->last_segment() ); - Geom::CubicBezier const * rev_cal2_lastseg = dynamic_cast<Geom::CubicBezier const *>( rev_cal2->last_segment() ); + Geom::BezierCurve const * dc_cal1_firstseg = dynamic_cast<Geom::BezierCurve const *>( this->cal1->first_segment() ); + Geom::BezierCurve const * rev_cal2_firstseg = dynamic_cast<Geom::BezierCurve const *>( rev_cal2->first_segment() ); + Geom::BezierCurve const * dc_cal1_lastseg = dynamic_cast<Geom::BezierCurve const *>( this->cal1->last_segment() ); + Geom::BezierCurve const * rev_cal2_lastseg = dynamic_cast<Geom::BezierCurve const *>( rev_cal2->last_segment() ); g_assert( dc_cal1_firstseg ); g_assert( rev_cal2_firstseg ); @@ -817,11 +812,21 @@ void EraserTool::accumulate() { this->accumulated->append(this->cal1, FALSE); - add_cap(this->accumulated, (*dc_cal1_lastseg)[2], (*dc_cal1_lastseg)[3], (*rev_cal2_firstseg)[0], (*rev_cal2_firstseg)[1], this->cap_rounding); + add_cap(this->accumulated, + dc_cal1_lastseg->finalPoint() - dc_cal1_lastseg->unitTangentAt(1), + dc_cal1_lastseg->finalPoint(), + rev_cal2_firstseg->initialPoint(), + rev_cal2_firstseg->initialPoint() + rev_cal2_firstseg->unitTangentAt(0), + this->cap_rounding); this->accumulated->append(rev_cal2, TRUE); - add_cap(this->accumulated, (*rev_cal2_lastseg)[2], (*rev_cal2_lastseg)[3], (*dc_cal1_firstseg)[0], (*dc_cal1_firstseg)[1], this->cap_rounding); + add_cap(this->accumulated, + rev_cal2_lastseg->finalPoint() - rev_cal2_lastseg->unitTangentAt(1), + rev_cal2_lastseg->finalPoint(), + dc_cal1_firstseg->initialPoint(), + dc_cal1_firstseg->initialPoint() + dc_cal1_firstseg->unitTangentAt(0), + this->cap_rounding); this->accumulated->closepath(); diff --git a/src/ui/tools/flood-tool.cpp b/src/ui/tools/flood-tool.cpp index bb8782dfa..748c82717 100644 --- a/src/ui/tools/flood-tool.cpp +++ b/src/ui/tools/flood-tool.cpp @@ -84,6 +84,28 @@ const std::string& FloodTool::getPrefsPath() { const std::string FloodTool::prefsPath = "/tools/paintbucket"; +// TODO: Replace by C++11 initialization +// Must match PaintBucketChannels enum +Glib::ustring ch_init[8] = { + _("Visible Colors"), + _("Red"), + _("Green"), + _("Blue"), + _("Hue"), + _("Saturation"), + _("Lightness"), + _("Alpha"), +}; +const std::vector<Glib::ustring> FloodTool::channel_list( ch_init, ch_init+8 ); + +Glib::ustring gap_init[4] = { + C_("Flood autogap", "None"), + C_("Flood autogap", "Small"), + C_("Flood autogap", "Medium"), + C_("Flood autogap", "Large") +}; +const std::vector<Glib::ustring> FloodTool::gap_list( gap_init, gap_init+4 ); + FloodTool::FloodTool() : ToolBase(cursor_paintbucket_xpm, 11, 30) , item(NULL) @@ -174,38 +196,6 @@ inline unsigned char * get_trace_pixel(guchar *trace_px, int x, int y, int width } /** - * Generate the list of trace channel selection entries. - */ -GList * flood_channels_dropdown_items_list() { - GList *glist = NULL; - - glist = g_list_append (glist, _("Visible Colors")); - glist = g_list_append (glist, _("Red")); - glist = g_list_append (glist, _("Green")); - glist = g_list_append (glist, _("Blue")); - glist = g_list_append (glist, _("Hue")); - glist = g_list_append (glist, _("Saturation")); - glist = g_list_append (glist, _("Lightness")); - glist = g_list_append (glist, _("Alpha")); - - return glist; -} - -/** - * Generate the list of autogap selection entries. - */ -GList * flood_autogap_dropdown_items_list() { - GList *glist = NULL; - - glist = g_list_append (glist, (void*) C_("Flood autogap", "None")); - glist = g_list_append (glist, (void*) C_("Flood autogap", "Small")); - glist = g_list_append (glist, (void*) C_("Flood autogap", "Medium")); - glist = g_list_append (glist, (void*) C_("Flood autogap", "Large")); - - return glist; -} - -/** * Compare a pixel in a pixel buffer with another pixel to determine if a point should be included in the fill operation. * @param check The pixel in the pixel buffer to check. * @param orig The original selected pixel to use as the fill target color. @@ -942,7 +932,7 @@ static void sp_flood_do_flood_fill(ToolBase *event_context, GdkEvent *event, boo std::deque<Geom::Point>::iterator start_sort = fill_queue.begin(); std::deque<Geom::Point>::iterator end_sort = fill_queue.begin(); unsigned int sort_y = (unsigned int)cp[Geom::Y]; - unsigned int current_y = sort_y; + unsigned int current_y; for (std::deque<Geom::Point>::iterator i = fill_queue.begin(); i != fill_queue.end(); ++i) { Geom::Point current = *i; diff --git a/src/ui/tools/flood-tool.h b/src/ui/tools/flood-tool.h index 5104a42e9..100875f22 100644 --- a/src/ui/tools/flood-tool.h +++ b/src/ui/tools/flood-tool.h @@ -13,6 +13,7 @@ #include <sigc++/connection.h> #include "ui/tools/tool-base.h" +#include <vector> #define SP_FLOOD_CONTEXT(obj) (dynamic_cast<Inkscape::UI::Tools::FloodTool*>((Inkscape::UI::Tools::ToolBase*)obj)) #define SP_IS_FLOOD_CONTEXT(obj) (dynamic_cast<const Inkscape::UI::Tools::FloodTool*>((const Inkscape::UI::Tools::ToolBase*)obj) != NULL) @@ -39,15 +40,14 @@ public: virtual const std::string& getPrefsPath(); static void set_channels(gint channels); + static const std::vector<Glib::ustring> channel_list; + static const std::vector<Glib::ustring> gap_list; private: void selection_changed(Inkscape::Selection* selection); void finishItem(); }; -GList* flood_channels_dropdown_items_list (void); -GList* flood_autogap_dropdown_items_list (void); - enum PaintBucketChannels { FLOOD_CHANNELS_RGB, FLOOD_CHANNELS_R, diff --git a/src/ui/tools/gradient-tool.cpp b/src/ui/tools/gradient-tool.cpp index b27859ebb..603458983 100644 --- a/src/ui/tools/gradient-tool.cpp +++ b/src/ui/tools/gradient-tool.cpp @@ -106,7 +106,7 @@ void GradientTool::selection_changed(Inkscape::Selection*) { if (selection == NULL) { return; } - guint n_obj = g_slist_length((GSList *) selection->itemList()); + guint n_obj = selection->itemList().size(); if (!drag->isNonEmpty() || selection->isEmpty()) return; @@ -207,7 +207,7 @@ sp_gradient_context_is_over_line (GradientTool *rc, SPItem *item, Geom::Point ev SPCtrlLine* line = SP_CTRLLINE(item); Geom::LineSegment ls(line->s, line->e); - Geom::Point nearest = ls.pointAt(ls.nearestPoint(rc->mousepoint_doc)); + Geom::Point nearest = ls.pointAt(ls.nearestTime(rc->mousepoint_doc)); double dist_screen = Geom::L2 (rc->mousepoint_doc - nearest) * desktop->current_zoom(); double tolerance = (double) SP_EVENT_CONTEXT(rc)->tolerance; @@ -492,10 +492,11 @@ bool GradientTool::root_handler(GdkEvent* event) { if (over_line) { // we take the first item in selection, because with doubleclick, the first click // always resets selection to the single object under cursor - sp_gradient_context_add_stop_near_point(this, SP_ITEM(selection->itemList()->data), this->mousepoint_doc, event->button.time); + sp_gradient_context_add_stop_near_point(this, SP_ITEM(selection->itemList().front()), this->mousepoint_doc, event->button.time); } else { - for (GSList const* i = selection->itemList(); i != NULL; i = i->next) { - SPItem *item = SP_ITEM(i->data); + std::vector<SPItem*> items=selection->itemList(); + for (std::vector<SPItem*>::const_iterator i = items.begin();i!=items.end();i++) { + SPItem *item = *i; SPGradientType new_type = (SPGradientType) prefs->getInt("/tools/gradient/newgradient", SP_GRADIENT_TYPE_LINEAR); Inkscape::PaintTarget fsmode = (prefs->getInt("/tools/gradient/newfillorstroke", 1) != 0) ? Inkscape::FOR_FILL : Inkscape::FOR_STROKE; @@ -898,32 +899,32 @@ static void sp_gradient_drag(GradientTool &rc, Geom::Point const pt, guint /*sta } else { // Starting from empty space: // Sort items so that the topmost comes last - GSList *items = g_slist_copy ((GSList *) selection->itemList()); - items = g_slist_sort(items, (GCompareFunc) sp_item_repr_compare_position); + std::vector<SPItem*> items(selection->itemList()); + sort(items.begin(),items.end(),sp_item_repr_compare_position); // take topmost - vector = sp_gradient_vector_for_object(document, desktop, SP_ITEM(g_slist_last(items)->data), fill_or_stroke); - g_slist_free (items); + vector = sp_gradient_vector_for_object(document, desktop, SP_ITEM(items.back()), fill_or_stroke); } // HACK: reset fill-opacity - that 0.75 is annoying; BUT remove this when we have an opacity slider for all tabs SPCSSAttr *css = sp_repr_css_attr_new(); sp_repr_css_set_property(css, "fill-opacity", "1.0"); - for (GSList const *i = selection->itemList(); i != NULL; i = i->next) { + std::vector<SPItem*> itemlist = selection->itemList(); + for (std::vector<SPItem*>::const_iterator i = itemlist.begin();i!=itemlist.end();i++) { //FIXME: see above - sp_repr_css_change_recursive(SP_OBJECT(i->data)->getRepr(), css, "style"); + sp_repr_css_change_recursive((*i)->getRepr(), css, "style"); - sp_item_set_gradient(SP_ITEM(i->data), vector, (SPGradientType) type, fill_or_stroke); + sp_item_set_gradient(*i, vector, (SPGradientType) type, fill_or_stroke); if (type == SP_GRADIENT_TYPE_LINEAR) { - sp_item_gradient_set_coords (SP_ITEM(i->data), POINT_LG_BEGIN, 0, rc.origin, fill_or_stroke, true, false); - sp_item_gradient_set_coords (SP_ITEM(i->data), POINT_LG_END, 0, pt, fill_or_stroke, true, false); + sp_item_gradient_set_coords (*i, POINT_LG_BEGIN, 0, rc.origin, fill_or_stroke, true, false); + sp_item_gradient_set_coords (*i, POINT_LG_END, 0, pt, fill_or_stroke, true, false); } else if (type == SP_GRADIENT_TYPE_RADIAL) { - sp_item_gradient_set_coords (SP_ITEM(i->data), POINT_RG_CENTER, 0, rc.origin, fill_or_stroke, true, false); - sp_item_gradient_set_coords (SP_ITEM(i->data), POINT_RG_R1, 0, pt, fill_or_stroke, true, false); + sp_item_gradient_set_coords (*i, POINT_RG_CENTER, 0, rc.origin, fill_or_stroke, true, false); + sp_item_gradient_set_coords (*i, POINT_RG_R1, 0, pt, fill_or_stroke, true, false); } - SP_OBJECT(i->data)->requestModified(SP_OBJECT_MODIFIED_FLAG); + (*i)->requestModified(SP_OBJECT_MODIFIED_FLAG); } if (ec->_grdrag) { ec->_grdrag->updateDraggers(); @@ -932,7 +933,7 @@ static void sp_gradient_drag(GradientTool &rc, Geom::Point const pt, guint /*sta ec->_grdrag->local_change = true; // give the grab out-of-bounds values of xp/yp because we're already dragging // and therefore are already out of tolerance - ec->_grdrag->grabKnot (SP_ITEM(selection->itemList()->data), + ec->_grdrag->grabKnot (selection->itemList()[0], type == SP_GRADIENT_TYPE_LINEAR? POINT_LG_END : POINT_RG_R1, -1, // ignore number (though it is always 1) fill_or_stroke, 99999, 99999, etime); @@ -941,7 +942,7 @@ static void sp_gradient_drag(GradientTool &rc, Geom::Point const pt, guint /*sta // status text; we do not track coords because this branch is run once, not all the time // during drag - int n_objects = g_slist_length((GSList *) selection->itemList()); + int n_objects = selection->itemList().size(); rc.message_context->setF(Inkscape::NORMAL_MESSAGE, ngettext("<b>Gradient</b> for %d object; with <b>Ctrl</b> to snap angle", "<b>Gradient</b> for %d objects; with <b>Ctrl</b> to snap angle", n_objects), diff --git a/src/ui/tools/lpe-tool.cpp b/src/ui/tools/lpe-tool.cpp index c0517578d..13e47f3a6 100644 --- a/src/ui/tools/lpe-tool.cpp +++ b/src/ui/tools/lpe-tool.cpp @@ -396,10 +396,10 @@ lpetool_create_measuring_items(LpeTool *lc, Inkscape::Selection *selection) SPCanvasGroup *tmpgrp = lc->desktop->getTempGroup(); gchar *arc_length; double lengthval; - - for (GSList const *i = selection->itemList(); i != NULL; i = i->next) { - if (SP_IS_PATH(i->data)) { - path = SP_PATH(i->data); + std::vector<SPItem*> items=selection->itemList(); + for(std::vector<SPItem*>::const_iterator i=items.begin();i!=items.end();i++){ + if (SP_IS_PATH(*i)) { + path = SP_PATH(*i); curve = path->getCurve(); Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2 = paths_to_pw(curve->get_pathvector()); canvas_text = (SPCanvasText *) sp_canvastext_new(tmpgrp, lc->desktop, Geom::Point(0,0), ""); diff --git a/src/ui/tools/measure-tool.cpp b/src/ui/tools/measure-tool.cpp index 0875c29e0..570f3e796 100644 --- a/src/ui/tools/measure-tool.cpp +++ b/src/ui/tools/measure-tool.cpp @@ -431,10 +431,10 @@ bool MeasureTool::root_handler(GdkEvent* event) { // TODO switch to a different variable name. The single letter 'l' is easy to misread. //select elements crossed by line segment: - GSList *items = desktop->getDocument()->getItemsAtPoints(desktop->dkey, points); + std::vector<SPItem*> items = desktop->getDocument()->getItemsAtPoints(desktop->dkey, points); std::vector<double> intersection_times; - for (GSList *l = items; l != NULL; l = l->next) { - SPItem *item = static_cast<SPItem*>(l->data); + for (std::vector<SPItem*>::const_iterator i=items.begin();i!=items.end();i++) { + SPItem *item = *i; if (SP_IS_SHAPE(item)) { calculate_intersections(desktop, item, lineseg, SP_SHAPE(item)->getCurve(), intersection_times); diff --git a/src/ui/tools/mesh-tool.cpp b/src/ui/tools/mesh-tool.cpp index 67ed7aef1..813d6ae5b 100644 --- a/src/ui/tools/mesh-tool.cpp +++ b/src/ui/tools/mesh-tool.cpp @@ -103,7 +103,7 @@ void MeshTool::selection_changed(Inkscape::Selection* /*sel*/) { return; } - guint n_obj = g_slist_length((GSList *) selection->itemList()); + guint n_obj = selection->itemList().size(); if (!drag->isNonEmpty() || selection->isEmpty()) { return; @@ -275,7 +275,7 @@ sp_mesh_context_is_over_line (MeshTool *rc, SPItem *item, Geom::Point event_p) SPCtrlCurve *curve = SP_CTRLCURVE(item); Geom::BezierCurveN<3> b( curve->p0, curve->p1, curve->p2, curve->p3 ); - Geom::Coord coord = b.nearestPoint( rc->mousepoint_doc ); // Coord == double + Geom::Coord coord = b.nearestTime( rc->mousepoint_doc ); // Coord == double Geom::Point nearest = b( coord ); double dist_screen = Geom::L2 (rc->mousepoint_doc - nearest) * desktop->current_zoom(); @@ -467,11 +467,12 @@ bool MeshTool::root_handler(GdkEvent* event) { if (over_line) { // We take the first item in selection, because with doubleclick, the first click // always resets selection to the single object under cursor - sp_mesh_context_split_near_point(this, SP_ITEM(selection->itemList()->data), this->mousepoint_doc, event->button.time); + sp_mesh_context_split_near_point(this, selection->itemList()[0], this->mousepoint_doc, event->button.time); } else { // Create a new gradient with default coordinates. - for (GSList const* i = selection->itemList(); i != NULL; i = i->next) { - SPItem *item = SP_ITEM(i->data); + std::vector<SPItem*> items=selection->itemList(); + for(std::vector<SPItem*>::const_iterator i=items.begin();i!=items.end();i++){ + SPItem *item = *i; SPGradientType new_type = SP_GRADIENT_TYPE_MESH; Inkscape::PaintTarget fsmode = (prefs->getInt("/tools/gradient/newfillorstroke", 1) != 0) ? Inkscape::FOR_FILL : Inkscape::FOR_STROKE; @@ -940,27 +941,27 @@ static void sp_mesh_drag(MeshTool &rc, Geom::Point const /*pt*/, guint /*state*/ } else { // Starting from empty space: // Sort items so that the topmost comes last - GSList *items = g_slist_copy ((GSList *) selection->itemList()); - items = g_slist_sort(items, (GCompareFunc) sp_item_repr_compare_position); + std::vector<SPItem*> items(selection->itemList()); + sort(items.begin(),items.end(),sp_item_repr_compare_position); // take topmost - vector = sp_gradient_vector_for_object(document, desktop, SP_ITEM(g_slist_last(items)->data), fill_or_stroke); - g_slist_free (items); + vector = sp_gradient_vector_for_object(document, desktop, SP_ITEM(items.back()), fill_or_stroke); } // HACK: reset fill-opacity - that 0.75 is annoying; BUT remove this when we have an opacity slider for all tabs SPCSSAttr *css = sp_repr_css_attr_new(); sp_repr_css_set_property(css, "fill-opacity", "1.0"); - for (GSList const *i = selection->itemList(); i != NULL; i = i->next) { + std::vector<SPItem*> items=selection->itemList(); + for(std::vector<SPItem*>::const_iterator i=items.begin();i!=items.end();i++){ //FIXME: see above - sp_repr_css_change_recursive(SP_OBJECT(i->data)->getRepr(), css, "style"); + sp_repr_css_change_recursive((*i)->getRepr(), css, "style"); - sp_item_set_gradient(SP_ITEM(i->data), vector, (SPGradientType) type, fill_or_stroke); + sp_item_set_gradient(*i, vector, (SPGradientType) type, fill_or_stroke); // We don't need to do anything. Mesh is already sized appropriately. - SP_OBJECT(i->data)->requestModified(SP_OBJECT_MODIFIED_FLAG); + (*i)->requestModified(SP_OBJECT_MODIFIED_FLAG); } // if (ec->_grdrag) { // ec->_grdrag->updateDraggers(); @@ -978,7 +979,7 @@ static void sp_mesh_drag(MeshTool &rc, Geom::Point const /*pt*/, guint /*state*/ // status text; we do not track coords because this branch is run once, not all the time // during drag - int n_objects = g_slist_length((GSList *) selection->itemList()); + int n_objects = selection->itemList().size(); rc.message_context->setF(Inkscape::NORMAL_MESSAGE, ngettext("<b>Gradient</b> for %d object; with <b>Ctrl</b> to snap angle", "<b>Gradient</b> for %d objects; with <b>Ctrl</b> to snap angle", n_objects), diff --git a/src/ui/tools/node-tool.cpp b/src/ui/tools/node-tool.cpp index a4b903960..6a6ca0b26 100644 --- a/src/ui/tools/node-tool.cpp +++ b/src/ui/tools/node-tool.cpp @@ -26,6 +26,8 @@ #include "ui/shape-editor.h" // temporary! #include "live_effects/effect.h" #include "display/curve.h" +#include "snap.h" +#include "sp-namedview.h" #include "sp-clippath.h" #include "sp-item-group.h" #include "sp-mask.h" @@ -112,7 +114,7 @@ namespace UI { namespace Tools { const std::string& NodeTool::getPrefsPath() { - return NodeTool::prefsPath; + return NodeTool::prefsPath; } const std::string NodeTool::prefsPath = "/tools/nodes"; @@ -216,7 +218,7 @@ void NodeTool::setup() { Inkscape::UI::ControlPoint::signal_mouseover_change.connect(sigc::mem_fun(this, &NodeTool::mouseover_changed)); this->_sizeUpdatedConn = ControlManager::getManager().connectCtrlSizeChanged( - sigc::mem_fun(this, &NodeTool::handleControlUiStyleChange) + sigc::mem_fun(this, &NodeTool::handleControlUiStyleChange) ); this->_selected_nodes = new Inkscape::UI::ControlPointSelection(this->desktop, this->_transform_handle_group); @@ -236,14 +238,14 @@ void NodeTool::setup() { ); this->_selected_nodes->signal_selection_changed.connect( - // Hide both signal parameters and bind the function parameter to 0 - // sigc::signal<void, SelectableControlPoint *, bool> - // <=> - // void update_tip(GdkEvent *event) - sigc::hide(sigc::hide(sigc::bind( - sigc::mem_fun(this, &NodeTool::update_tip), - (GdkEvent*)NULL - ))) + // Hide both signal parameters and bind the function parameter to 0 + // sigc::signal<void, SelectableControlPoint *, bool> + // <=> + // void update_tip(GdkEvent *event) + sigc::hide(sigc::hide(sigc::bind( + sigc::mem_fun(this, &NodeTool::update_tip), + (GdkEvent*)NULL + ))) ); this->helperpath_tmpitem = NULL; @@ -359,7 +361,7 @@ void NodeTool::set(const Inkscape::Preferences::Entry& value) { this->edit_masks = value.getBool(); this->selection_changed(this->desktop->selection); } else { - ToolBase::set(value); + ToolBase::set(value); } } @@ -370,7 +372,7 @@ void gather_items(NodeTool *nt, SPItem *base, SPObject *obj, Inkscape::UI::Shape using namespace Inkscape::UI; if (!obj) { - return; + return; } //XML Tree being used directly here while it shouldn't be. @@ -410,10 +412,9 @@ void NodeTool::selection_changed(Inkscape::Selection *sel) { std::set<ShapeRecord> shapes; - GSList const *ilist = sel->itemList(); - - for (GSList *i = const_cast<GSList*>(ilist); i; i = i->next) { - SPObject *obj = static_cast<SPObject*>(i->data); + std::vector<SPItem*> items=sel->itemList(); + for(std::vector<SPItem*>::const_iterator i=items.begin();i!=items.end();i++){ + SPObject *obj = *i; if (SP_IS_ITEM(obj)) { gather_items(this, NULL, static_cast<SPItem*>(obj), SHAPE_ROLE_NORMAL, shapes); @@ -447,6 +448,9 @@ void NodeTool::selection_changed(Inkscape::Selection *sel) { } } + _previous_selection = _current_selection; + _current_selection = sel->itemList(); + this->_multipath->setItems(shapes); this->update_tip(NULL); this->desktop->updateNow(); @@ -459,31 +463,47 @@ bool NodeTool::root_handler(GdkEvent* event) { * 3. some keybindings */ using namespace Inkscape::UI; // pull in event helpers - + Inkscape::Selection *selection = desktop->selection; static Inkscape::Preferences *prefs = Inkscape::Preferences::get(); if (this->_multipath->event(this, event)) { - return true; + return true; } if (this->_selector->event(this, event)) { - return true; + return true; } if (this->_selected_nodes->event(this, event)) { - return true; + return true; } switch (event->type) { case GDK_MOTION_NOTIFY: { - this->update_helperpath(); + this->update_helperpath(); combine_motion_events(desktop->canvas, event->motion, 0); this->update_helperpath(); SPItem *over_item = sp_event_context_find_item (desktop, event_point(event->button), FALSE, TRUE); + Geom::Point const motion_w(event->motion.x, event->motion.y); + Geom::Point const motion_dt(this->desktop->w2d(motion_w)); + + SnapManager &m = this->desktop->namedview->snap_manager; + + // We will show a pre-snap indication for when the user adds a node through double-clicking + // Adding a node will only work when a path has been selected; if that's not the case then snapping is useless + if (not this->desktop->selection->isEmpty()) { + if (!(event->motion.state & GDK_SHIFT_MASK)) { + m.setup(this->desktop); + Inkscape::SnapCandidatePoint scp(motion_dt, Inkscape::SNAPSOURCE_OTHER_HANDLE); + m.preSnap(scp, true); + m.unSetup(); + } + } + if (over_item != this->_last_over) { this->_last_over = over_item; //ink_node_tool_update_tip(nt, event); @@ -492,11 +512,11 @@ bool NodeTool::root_handler(GdkEvent* event) { // create pathflash outline if (prefs->getBool("/tools/nodes/pathflash_enabled")) { if (over_item == this->flashed_item) { - break; + break; } if (!prefs->getBool("/tools/nodes/pathflash_selected") && selection->includes(over_item)) { - break; + break; } if (this->flash_tempitem) { @@ -506,14 +526,14 @@ bool NodeTool::root_handler(GdkEvent* event) { } if (!SP_IS_SHAPE(over_item)) { - break; // for now, handle only shapes + break; // for now, handle only shapes } this->flashed_item = over_item; SPCurve *c = SP_SHAPE(over_item)->getCurveBeforeLPE(); if (!c) { - break; // break out when curve doesn't exist + break; // break out when curve doesn't exist } c->transform(over_item->i2dt_affine()); @@ -576,11 +596,42 @@ bool NodeTool::root_handler(GdkEvent* event) { case GDK_KEY_RELEASE: //ink_node_tool_update_tip(nt, event); - this->update_tip(event); + this->update_tip(event); + break; + + case GDK_BUTTON_RELEASE: + if (this->_selector->doubleClicked()) { + // If the selector received the doubleclick event, then we're at some distance from + // the path; otherwise, the doubleclick event would have been received by + // CurveDragPoint; we will insert nodes into the path anyway but only if we can snap + // to the path. Otherwise the position would not be very well defined. + if (!(event->motion.state & GDK_SHIFT_MASK)) { + Geom::Point const motion_w(event->motion.x, event->motion.y); + Geom::Point const motion_dt(this->desktop->w2d(motion_w)); + + SnapManager &m = this->desktop->namedview->snap_manager; + m.setup(this->desktop); + Inkscape::SnapCandidatePoint scp(motion_dt, Inkscape::SNAPSOURCE_OTHER_HANDLE); + Inkscape::SnappedPoint sp = m.freeSnap(scp, Geom::OptRect(), true); + m.unSetup(); + + if (sp.getSnapped()) { + // The first click of the double click will have cleared the path selection, because + // we clicked aside of the path. We need to undo this on double click + Inkscape::Selection *selection = desktop->getSelection(); + selection->addList(_previous_selection); + + // The selection has been restored, and the signal selection_changed has been emitted, + // which has again forced a restore of the _mmap variable of the MultiPathManipulator (this->_multipath) + // Now we can insert the new nodes as if nothing has happened! + this->_multipath->insertNode(this->desktop->d2w(sp.getPoint())); + } + } + } break; default: - break; + break; } return ToolBase::root_handler(event); @@ -593,7 +644,7 @@ void NodeTool::update_tip(GdkEvent *event) { unsigned new_state = state_after_event(event); if (new_state == event->key.state) { - return; + return; } if (state_held_shift(new_state)) { @@ -614,9 +665,27 @@ void NodeTool::update_tip(GdkEvent *event) { unsigned total = this->_selected_nodes->allPoints().size(); if (sz != 0) { - char *nodestring = g_strdup_printf( - ngettext("<b>%u of %u</b> node selected.", "<b>%u of %u</b> nodes selected.", total), - sz, total); + char *nodestring; + if (sz == 2) { + // if there are only two nodes selected, display the angle + // of a line going through them relative to the X axis. + Inkscape::UI::ControlPointSelection::Set &selection_nodes = this->_selected_nodes->allPoints(); + std::vector<Geom::Point> positions; + for (Inkscape::UI::ControlPointSelection::Set::iterator i = selection_nodes.begin(); i != selection_nodes.end(); ++i) { + if ((*i)->selected()) { + Inkscape::UI::Node *n = dynamic_cast<Inkscape::UI::Node *>(*i); + positions.push_back(n->position()); + } + } + g_assert(positions.size() == 2); + const double angle = Geom::rad_to_deg(Geom::Line(positions[0], positions[1]).angle()); + nodestring = g_strdup_printf("<b>%u of %u</b> nodes selected, angle: %.2f°.", sz, total, angle); + } + else { + nodestring = g_strdup_printf( + ngettext("<b>%u of %u</b> node selected.", "<b>%u of %u</b> nodes selected.", total), + sz, total); + } if (this->_last_over) { // TRANSLATORS: The %s below is where the "%u of %u nodes selected" sentence gets put @@ -658,12 +727,11 @@ void NodeTool::select_area(Geom::Rect const &sel, GdkEventButton *event) { if (this->_multipath->empty()) { // if multipath is empty, select rubberbanded items rather than nodes Inkscape::Selection *selection = this->desktop->selection; - GSList *items = this->desktop->getDocument()->getItemsInBox(this->desktop->dkey, sel); + std::vector<SPItem*> items = this->desktop->getDocument()->getItemsInBox(this->desktop->dkey, sel); selection->setList(items); - g_slist_free(items); } else { if (!held_shift(*event)) { - this->_selected_nodes->clear(); + this->_selected_nodes->clear(); } this->_selected_nodes->selectArea(sel); @@ -674,11 +742,11 @@ void NodeTool::select_point(Geom::Point const &/*sel*/, GdkEventButton *event) { using namespace Inkscape::UI; // pull in event helpers if (!event) { - return; + return; } if (event->button != 1) { - return; + return; } Inkscape::Selection *selection = this->desktop->selection; diff --git a/src/ui/tools/node-tool.h b/src/ui/tools/node-tool.h index 20375e869..8342d66a6 100644 --- a/src/ui/tools/node-tool.h +++ b/src/ui/tools/node-tool.h @@ -19,18 +19,18 @@ #include "selection.h" namespace Inkscape { - namespace Display { - class TemporaryItem; - } - - namespace UI { - class MultiPathManipulator; - class ControlPointSelection; - class Selector; - class ControlPoint; - - struct PathSharedData; - } + namespace Display { + class TemporaryItem; + } + + namespace UI { + class MultiPathManipulator; + class ControlPointSelection; + class Selector; + class ControlPoint; + + struct PathSharedData; + } } struct SPCanvasGroup; @@ -44,26 +44,26 @@ namespace Tools { class NodeTool : public ToolBase { public: - NodeTool(); - virtual ~NodeTool(); + NodeTool(); + virtual ~NodeTool(); - Inkscape::UI::ControlPointSelection* _selected_nodes; + Inkscape::UI::ControlPointSelection* _selected_nodes; Inkscape::UI::MultiPathManipulator* _multipath; bool edit_clipping_paths; bool edit_masks; - static const std::string prefsPath; + static const std::string prefsPath; - virtual void setup(); - virtual void update_helperpath(); - virtual void set(const Inkscape::Preferences::Entry& val); - virtual bool root_handler(GdkEvent* event); + virtual void setup(); + virtual void update_helperpath(); + virtual void set(const Inkscape::Preferences::Entry& val); + virtual bool root_handler(GdkEvent* event); - virtual const std::string& getPrefsPath(); + virtual const std::string& getPrefsPath(); private: - sigc::connection _selection_changed_connection; + sigc::connection _selection_changed_connection; sigc::connection _mouseover_changed_connection; sigc::connection _sizeUpdatedConn; @@ -85,13 +85,16 @@ private: bool show_transform_handles; bool single_node_transform_handles; - void selection_changed(Inkscape::Selection *sel); + std::vector<SPItem*> _current_selection; + std::vector<SPItem*> _previous_selection; - void select_area(Geom::Rect const &sel, GdkEventButton *event); - void select_point(Geom::Point const &sel, GdkEventButton *event); - void mouseover_changed(Inkscape::UI::ControlPoint *p); - void update_tip(GdkEvent *event); - void handleControlUiStyleChange(); + void selection_changed(Inkscape::Selection *sel); + + void select_area(Geom::Rect const &sel, GdkEventButton *event); + void select_point(Geom::Point const &sel, GdkEventButton *event); + void mouseover_changed(Inkscape::UI::ControlPoint *p); + void update_tip(GdkEvent *event); + void handleControlUiStyleChange(); }; } diff --git a/src/ui/tools/pen-tool.cpp b/src/ui/tools/pen-tool.cpp index daffc7032..827dbf5c3 100644 --- a/src/ui/tools/pen-tool.cpp +++ b/src/ui/tools/pen-tool.cpp @@ -57,8 +57,7 @@ #include <typeinfo> #include <2geom/pathvector.h> #include <2geom/affine.h> -#include <2geom/bezier-curve.h> -#include <2geom/hvlinesegment.h> +#include <2geom/curves.h> #include "helper/geom-nodetype.h" #include "helper/geom-curves.h" @@ -71,7 +70,7 @@ #define INKSCAPE_LPE_BSPLINE_C #include "live_effects/lpe-bspline.h" -#include <2geom/nearest-point.h> +#include <2geom/nearest-time.h> #include "live_effects/effect.h" @@ -762,14 +761,12 @@ bool PenTool::_handleButtonRelease(GdkEventButton const &revent) { } } this->state = PenTool::CONTROL; - ret = true; break; case PenTool::CONTROL: // End current segment this->_endpointSnap(p, revent.state); this->_finishSegment(p, revent.state); this->state = PenTool::POINT; - ret = true; break; case PenTool::CLOSE: // End current segment @@ -783,12 +780,10 @@ bool PenTool::_handleButtonRelease(GdkEventButton const &revent) { } this->_finish(true); this->state = PenTool::POINT; - ret = true; break; case PenTool::STOP: // This is allowed, if we just canceled curve this->state = PenTool::POINT; - ret = true; break; default: break; @@ -823,7 +818,6 @@ bool PenTool::_handleButtonRelease(GdkEventButton const &revent) { break; } this->state = PenTool::POINT; - ret = true; break; default: break; @@ -1357,8 +1351,9 @@ void PenTool::_bsplineSpiroColor() void PenTool::_bsplineSpiro(bool shift) { - if(!this->spiro && !this->bspline) + if(!this->spiro && !this->bspline){ return; + } shift?this->_bsplineSpiroOff():this->_bsplineSpiroOn(); this->_bsplineSpiroBuild(); @@ -1419,21 +1414,26 @@ void PenTool::_bsplineSpiroStartAnchor(bool shift) }else{ this->spiro = false; } - if(!this->spiro && !this->bspline) + if(!this->spiro && !this->bspline){ + SPCurve *tmp_curve = this->sa->curve->copy(); + if (this->sa->start) { + tmp_curve = tmp_curve ->create_reverse(); + } + this->overwrite_curve = tmp_curve ; return; - - if(shift) + } + if(shift){ this->_bsplineSpiroStartAnchorOff(); - else + } else { this->_bsplineSpiroStartAnchorOn(); + } } void PenTool::_bsplineSpiroStartAnchorOn() { using Geom::X; using Geom::Y; - SPCurve *tmp_curve = new SPCurve(); - tmp_curve = this->sa->curve->copy(); + SPCurve *tmp_curve = this->sa->curve->copy(); if(this->sa->start) tmp_curve = tmp_curve ->create_reverse(); Geom::CubicBezier const * cubic = dynamic_cast<Geom::CubicBezier const*>(&*tmp_curve ->last_segment()); @@ -1465,8 +1465,7 @@ void PenTool::_bsplineSpiroStartAnchorOn() void PenTool::_bsplineSpiroStartAnchorOff() { - SPCurve *tmp_curve = new SPCurve(); - tmp_curve = this->sa->curve->copy(); + SPCurve *tmp_curve = this->sa->curve->copy(); if(this->sa->start) tmp_curve = tmp_curve ->create_reverse(); Geom::CubicBezier const * cubic = dynamic_cast<Geom::CubicBezier const*>(&*tmp_curve ->last_segment()); @@ -1490,9 +1489,9 @@ void PenTool::_bsplineSpiroStartAnchorOff() } void PenTool::_bsplineSpiroMotion(bool shift){ - if(!this->spiro && !this->bspline) + if(!this->spiro && !this->bspline){ return; - + } using Geom::X; using Geom::Y; if(this->red_curve->is_empty()) return; @@ -1522,7 +1521,7 @@ void PenTool::_bsplineSpiroMotion(bool shift){ Geom::D2< Geom::SBasis > SBasisweight_power; weight_power->moveto(tmp_curve ->last_segment()->finalPoint()); weight_power->lineto(tmp_curve ->last_segment()->initialPoint()); - float WP = Geom::nearest_point((*cubic)[2],*weight_power->first_segment()); + float WP = Geom::nearest_time((*cubic)[2],*weight_power->first_segment()); weight_power->reset(); weight_power->moveto(this->red_curve->last_segment()->initialPoint()); weight_power->lineto(this->red_curve->last_segment()->finalPoint()); @@ -1564,7 +1563,7 @@ void PenTool::_bsplineSpiroEndAnchorOn() using Geom::Y; this->p[2] = this->p[3] + (1./3)*(this->p[0] - this->p[3]); this->p[2] = Geom::Point(this->p[2][X] + HANDLE_CUBIC_GAP,this->p[2][Y] + HANDLE_CUBIC_GAP); - SPCurve *tmp_curve = new SPCurve(); + SPCurve *tmp_curve; SPCurve *last_segment = new SPCurve(); Geom::Point point_c(0,0); bool reverse = false; @@ -1621,7 +1620,7 @@ void PenTool::_bsplineSpiroEndAnchorOn() void PenTool::_bsplineSpiroEndAnchorOff() { - SPCurve *tmp_curve = new SPCurve(); + SPCurve *tmp_curve; SPCurve *last_segment = new SPCurve(); bool reverse = false; this->p[2] = this->p[3]; @@ -1744,7 +1743,7 @@ void PenTool::_bsplineSpiroBuild() //from LPE BSPLINE: void PenTool::_bsplineDoEffect(SPCurve * curve) { - const double NO_POWER = 0.0; + //const double NO_POWER = 0.0; const double DEFAULT_START_POWER = 0.3334; const double DEFAULT_END_POWER = 0.6667; if (curve->get_segment_count() < 1) { @@ -1798,12 +1797,12 @@ void PenTool::_bsplineDoEffect(SPCurve * curve) if(are_near((*cubic)[1],(*cubic)[0]) && !are_near((*cubic)[2],(*cubic)[3])) { point_at1 = sbasis_in.valueAt(DEFAULT_START_POWER); } else { - point_at1 = sbasis_in.valueAt(Geom::nearest_point((*cubic)[1], *in->first_segment())); + point_at1 = sbasis_in.valueAt(Geom::nearest_time((*cubic)[1], *in->first_segment())); } if(are_near((*cubic)[2],(*cubic)[3]) && !are_near((*cubic)[1],(*cubic)[0])) { point_at2 = sbasis_in.valueAt(DEFAULT_END_POWER); } else { - point_at2 = sbasis_in.valueAt(Geom::nearest_point((*cubic)[2], *in->first_segment())); + point_at2 = sbasis_in.valueAt(Geom::nearest_time((*cubic)[2], *in->first_segment())); } } else { point_at1 = in->first_segment()->initialPoint(); @@ -1821,7 +1820,7 @@ void PenTool::_bsplineDoEffect(SPCurve * curve) if(are_near((*cubic)[1],(*cubic)[0]) && !are_near((*cubic)[2],(*cubic)[3])) { next_point_at1 = sbasis_in.valueAt(DEFAULT_START_POWER); } else { - next_point_at1 = sbasis_out.valueAt(Geom::nearest_point((*cubic)[1], *out->first_segment())); + next_point_at1 = sbasis_out.valueAt(Geom::nearest_time((*cubic)[1], *out->first_segment())); } } else { next_point_at1 = out->first_segment()->initialPoint(); @@ -1838,7 +1837,7 @@ void PenTool::_bsplineDoEffect(SPCurve * curve) cubic = dynamic_cast<Geom::CubicBezier const *>(&*path_it->begin()); if (cubic) { line_helper->moveto(sbasis_start.valueAt( - Geom::nearest_point((*cubic)[1], *start->first_segment()))); + Geom::nearest_time((*cubic)[1], *start->first_segment()))); } else { line_helper->moveto(start->first_segment()->initialPoint()); } @@ -1852,7 +1851,7 @@ void PenTool::_bsplineDoEffect(SPCurve * curve) cubic = dynamic_cast<Geom::CubicBezier const *>(&*curve_it1); if (cubic) { line_helper->lineto(sbasis_end.valueAt( - Geom::nearest_point((*cubic)[2], *end->first_segment()))); + Geom::nearest_time((*cubic)[2], *end->first_segment()))); } else { line_helper->lineto(end->first_segment()->finalPoint()); } diff --git a/src/ui/tools/pencil-tool.cpp b/src/ui/tools/pencil-tool.cpp index 008804162..16c26546f 100644 --- a/src/ui/tools/pencil-tool.cpp +++ b/src/ui/tools/pencil-tool.cpp @@ -53,6 +53,7 @@ static Geom::Point pencil_drag_origin_w(0, 0); static bool pencil_within_tolerance = false; static bool in_svg_plane(Geom::Point const &p) { return Geom::LInfty(p) < 1e18; } +const double HANDLE_CUBIC_GAP = 0.01; const std::string& PencilTool::getPrefsPath() { return PencilTool::prefsPath; @@ -357,7 +358,6 @@ bool PencilTool::_handleButtonRelease(GdkEventButton const &revent) { // Ctrl+click creates a single point so only set context in ADDLINE mode when Ctrl isn't pressed this->state = SP_PENCIL_CONTEXT_ADDLINE; } - ret = true; break; case SP_PENCIL_CONTEXT_ADDLINE: /* Finish segment now */ @@ -371,7 +371,6 @@ bool PencilTool::_handleButtonRelease(GdkEventButton const &revent) { this->_finishEndpoint(); this->state = SP_PENCIL_CONTEXT_IDLE; sp_event_context_discard_delayed_snap_event(this); - ret = true; break; case SP_PENCIL_CONTEXT_FREEHAND: if (revent.state & GDK_MOD1_MASK) { @@ -413,7 +412,6 @@ bool PencilTool::_handleButtonRelease(GdkEventButton const &revent) { // reset sketch mode too this->sketch_n = 0; } - ret = true; break; case SP_PENCIL_CONTEXT_SKETCH: default: @@ -663,10 +661,10 @@ void PencilTool::_interpolate() { for (int c = 0; c < n_segs; c++) { // if we are in BSpline we modify the trace to create adhoc nodes if(mode == 2){ - Geom::Point point_at1 = b[4*c+0] + (1./3)*(b[4*c+3] - b[4*c+0]); - point_at1 = Geom::Point(point_at1[X] + 0.0001,point_at1[Y] + 0.0001); - Geom::Point point_at2 = b[4*c+3] + (1./3)*(b[4*c+0] - b[4*c+3]); - point_at2 = Geom::Point(point_at2[X] + 0.0001,point_at2[Y] + 0.0001); + Geom::Point point_at1 = b[4 * c + 0] + (1./3) * (b[4 * c + 3] - b[4 * c + 0]); + point_at1 = Geom::Point(point_at1[X] + HANDLE_CUBIC_GAP, point_at1[Y] + HANDLE_CUBIC_GAP); + Geom::Point point_at2 = b[4 * c + 3] + (1./3) * (b[4 * c + 0] - b[4 * c + 3]); + point_at2 = Geom::Point(point_at2[X] + HANDLE_CUBIC_GAP, point_at2[Y] + HANDLE_CUBIC_GAP); this->green_curve->curveto(point_at1,point_at2,b[4*c+3]); }else{ this->green_curve->curveto(b[4 * c + 1], b[4 * c + 2], b[4 * c + 3]); @@ -811,9 +809,9 @@ void PencilTool::_fitAndSplit() { guint mode = prefs->getInt("/tools/freehand/pencil/freehand-mode", 0); if(mode == 2){ Geom::Point point_at1 = b[0] + (1./3)*(b[3] - b[0]); - point_at1 = Geom::Point(point_at1[X] + 0.0001,point_at1[Y] + 0.0001); + point_at1 = Geom::Point(point_at1[X] + HANDLE_CUBIC_GAP, point_at1[Y] + HANDLE_CUBIC_GAP); Geom::Point point_at2 = b[3] + (1./3)*(b[0] - b[3]); - point_at2 = Geom::Point(point_at2[X] + 0.0001,point_at2[Y] + 0.0001); + point_at2 = Geom::Point(point_at2[X] + HANDLE_CUBIC_GAP, point_at2[Y] + HANDLE_CUBIC_GAP); this->red_curve->curveto(point_at1,point_at2,b[3]); }else{ this->red_curve->curveto(b[1], b[2], b[3]); diff --git a/src/ui/tools/rect-tool.cpp b/src/ui/tools/rect-tool.cpp index 62a9006ea..844965c4d 100644 --- a/src/ui/tools/rect-tool.cpp +++ b/src/ui/tools/rect-tool.cpp @@ -143,7 +143,6 @@ bool RectTool::item_handler(SPItem* item, GdkEvent* event) { case GDK_BUTTON_PRESS: if ( event->button.button == 1 && !this->space_panning) { Inkscape::setup_for_drag_start(desktop, this, event); - ret = TRUE; } break; // motion and release are always on root (why?) diff --git a/src/ui/tools/select-tool.cpp b/src/ui/tools/select-tool.cpp index 40b994968..f06b03d91 100644 --- a/src/ui/tools/select-tool.cpp +++ b/src/ui/tools/select-tool.cpp @@ -472,7 +472,7 @@ bool SelectTool::root_handler(GdkEvent* event) { case GDK_2BUTTON_PRESS: if (event->button.button == 1) { if (!selection->isEmpty()) { - SPItem *clicked_item = static_cast<SPItem *>(selection->itemList()->data); + SPItem *clicked_item = selection->itemList()[0]; if (dynamic_cast<SPGroup *>(clicked_item) && !dynamic_cast<SPBox3D *>(clicked_item)) { // enter group if it's not a 3D box desktop->setCurrentLayer(clicked_item); @@ -709,7 +709,7 @@ bool SelectTool::root_handler(GdkEvent* event) { if (r->is_started() && !within_tolerance) { // this was a rubberband drag - GSList *items = NULL; + std::vector<SPItem*> items; if (r->getMode() == RUBBERBAND_MODE_RECT) { Geom::OptRect const b = r->getRectangle(); @@ -730,7 +730,6 @@ bool SelectTool::root_handler(GdkEvent* event) { selection->setList (items); } - g_slist_free (items); } else { // it was just a click, or a too small rubberband r->stop(); @@ -778,7 +777,6 @@ bool SelectTool::root_handler(GdkEvent* event) { } rb_escaped = 0; - ret = TRUE; } } } diff --git a/src/ui/tools/spray-tool.cpp b/src/ui/tools/spray-tool.cpp index 0399b1e55..e2be5ca4b 100644 --- a/src/ui/tools/spray-tool.cpp +++ b/src/ui/tools/spray-tool.cpp @@ -133,14 +133,13 @@ SprayTool::SprayTool() : ToolBase(cursor_spray_xpm, 4, 4, false) , pressure(TC_DEFAULT_PRESSURE) , dragging(false) - , usepressure(0) - , usetilt(0) + , usepressure(false) + , usetilt(false) , usetext(false) , width(0.2) , ratio(0) , tilt(0) , rotation_variation(0) - , force(0.2) , population(0) , scale_variation(1) , scale(1) @@ -165,20 +164,12 @@ SprayTool::~SprayTool() { } } -static bool is_transform_modes(gint mode) -{ - return (mode == SPRAY_MODE_COPY || - mode == SPRAY_MODE_CLONE || - mode == SPRAY_MODE_SINGLE_PATH || - mode == SPRAY_OPTION); -} - void SprayTool::update_cursor(bool /*with_shift*/) { guint num = 0; gchar *sel_message = NULL; if (!desktop->selection->isEmpty()) { - num = g_slist_length(const_cast<GSList *>(desktop->selection->itemList())); + num = desktop->selection->itemList().size(); sel_message = g_strdup_printf(ngettext("<b>%i</b> object selected","<b>%i</b> objects selected",num), num); } else { sel_message = g_strdup_printf("%s", _("<b>Nothing</b> selected")); @@ -207,8 +198,7 @@ void SprayTool::setup() { { /* TODO: have a look at sp_dyna_draw_context_setup where the same is done.. generalize? at least make it an arcto! */ - Geom::PathVector path; - Geom::Circle(0, 0, 1).getPath(path); + Geom::PathVector path = Geom::Path(Geom::Circle(0,0,1)); SPCurve *c = new SPCurve(path); @@ -229,7 +219,6 @@ void SprayTool::setup() { sp_event_context_read(this, "scale_variation"); sp_event_context_read(this, "mode"); sp_event_context_read(this, "population"); - sp_event_context_read(this, "force"); sp_event_context_read(this, "mean"); sp_event_context_read(this, "standard_deviation"); sp_event_context_read(this, "usepressure"); @@ -271,9 +260,7 @@ void SprayTool::set(const Inkscape::Preferences::Entry& val) { this->tilt = CLAMP(val.getDouble(0.1), 0, 1000.0); } else if (path == "ratio") { this->ratio = CLAMP(val.getDouble(), 0.0, 0.9); - } else if (path == "force") { - this->force = CLAMP(val.getDouble(1.0), 0, 1.0); - } + } } static void sp_spray_extinput(SprayTool *tc, GdkEvent *event) @@ -290,16 +277,6 @@ static double get_dilate_radius(SprayTool *tc) return 250 * tc->width/SP_EVENT_CONTEXT(tc)->desktop->current_zoom(); } -static double get_path_force(SprayTool *tc) -{ - double force = 8 * (tc->usepressure? tc->pressure : TC_DEFAULT_PRESSURE) - /sqrt(SP_EVENT_CONTEXT(tc)->desktop->current_zoom()); - if (force > 3) { - force += 4 * (force - 3); - } - return force * tc->force; -} - static double get_path_mean(SprayTool *tc) { return tc->mean; @@ -310,10 +287,11 @@ static double get_path_standard_deviation(SprayTool *tc) return tc->standard_deviation; } -static double get_move_force(SprayTool *tc) +static double get_population(SprayTool *tc) { - double force = (tc->usepressure? tc->pressure : TC_DEFAULT_PRESSURE); - return force * tc->force; + double pressure = (tc->usepressure? tc->pressure / TC_DEFAULT_PRESSURE : 1); + //g_warning("Pressure, population: %f, %f", pressure, pressure * tc->population); + return pressure * tc->population; } static double get_move_mean(SprayTool *tc) @@ -361,7 +339,6 @@ static bool sp_spray_recursive(SPDesktop *desktop, Geom::Point /*vector*/, gint mode, double radius, - double /*force*/, double population, double &scale, double scale_variation, @@ -426,11 +403,9 @@ static bool sp_spray_recursive(SPDesktop *desktop, SPItem *unionResult = NULL; // Previous union int i=1; - for (GSList *items = g_slist_copy(const_cast<GSList *>(selection->itemList())); - items != NULL; - items = items->next) { - - SPItem *item1 = dynamic_cast<SPItem *>(static_cast<SPObject *>(items->data)); + std::vector<SPItem*> items=selection->itemList(); + for(std::vector<SPItem*>::const_iterator it=items.begin();it!=items.end();it++){ + SPItem *item1 = *it; if (i == 1) { parent_item = item1; } @@ -527,8 +502,8 @@ static bool sp_spray_dilate(SprayTool *tc, Geom::Point /*event_p*/, Geom::Point bool did = false; double radius = get_dilate_radius(tc); - double path_force = get_path_force(tc); - if (radius == 0 || path_force == 0) { + double population = get_population(tc); + if (radius == 0 || population == 0) { return false; } double path_mean = get_path_mean(tc); @@ -539,42 +514,29 @@ static bool sp_spray_dilate(SprayTool *tc, Geom::Point /*event_p*/, Geom::Point if (radius == 0 || path_standard_deviation == 0) { return false; } - double move_force = get_move_force(tc); double move_mean = get_move_mean(tc); double move_standard_deviation = get_move_standard_deviation(tc); { - GSList *const original_selection = g_slist_copy(const_cast<GSList *>(selection->itemList())); + std::vector<SPItem*> const items(selection->itemList()); - for (GSList *items = original_selection; - items != NULL; - items = items->next) { - SPItem *item = dynamic_cast<SPItem *>(static_cast<SPObject *>(items->data)); + for(std::vector<SPItem*>::const_iterator i=items.begin();i!=items.end();i++){ + SPItem *item = *i; g_assert(item != NULL); sp_object_ref(item); } - for (GSList *items = original_selection; - items != NULL; - items = items->next) { - SPItem *item = dynamic_cast<SPItem *>(static_cast<SPObject *>(items->data)); + for(std::vector<SPItem*>::const_iterator i=items.begin();i!=items.end();i++){ + SPItem *item = *i; g_assert(item != NULL); - if (is_transform_modes(tc->mode)) { - if (sp_spray_recursive(desktop, selection, item, p, vector, tc->mode, radius, move_force, tc->population, tc->scale, tc->scale_variation, reverse, move_mean, move_standard_deviation, tc->ratio, tc->tilt, tc->rotation_variation, tc->distrib)) { - did = true; - } - } else { - if (sp_spray_recursive(desktop, selection, item, p, vector, tc->mode, radius, path_force, tc->population, tc->scale, tc->scale_variation, reverse, path_mean, path_standard_deviation, tc->ratio, tc->tilt, tc->rotation_variation, tc->distrib)) { - did = true; - } + if (sp_spray_recursive(desktop, selection, item, p, vector, tc->mode, radius, population, tc->scale, tc->scale_variation, reverse, move_mean, move_standard_deviation, tc->ratio, tc->tilt, tc->rotation_variation, tc->distrib)) { + did = true; } } - for (GSList *items = original_selection; - items != NULL; - items = items->next) { - SPItem *item = dynamic_cast<SPItem *>(static_cast<SPObject *>(items->data)); + for(std::vector<SPItem*>::const_iterator i=items.begin();i!=items.end();i++){ + SPItem *item = *i; g_assert(item != NULL); sp_object_unref(item); } @@ -650,7 +612,7 @@ bool SprayTool::root_handler(GdkEvent* event) { guint num = 0; if (!desktop->selection->isEmpty()) { - num = g_slist_length(const_cast<GSList *>(desktop->selection->itemList())); + num = desktop->selection->itemList().size(); } if (num == 0) { this->message_context->flash(Inkscape::ERROR_MESSAGE, _("<b>Nothing selected!</b> Select objects to spray.")); diff --git a/src/ui/tools/spray-tool.h b/src/ui/tools/spray-tool.h index 1a8f98006..8df730201 100644 --- a/src/ui/tools/spray-tool.h +++ b/src/ui/tools/spray-tool.h @@ -52,8 +52,8 @@ enum { class SprayTool : public ToolBase { public: - SprayTool(); - virtual ~SprayTool(); + SprayTool(); + virtual ~SprayTool(); //ToolBase event_context; //Inkscape::UI::Dialog::Dialog *dialog_option;//Attribut de type SprayOptionClass, localisé dans scr/ui/dialog @@ -70,7 +70,6 @@ public: double ratio; double tilt; double rotation_variation; - double force; double population; double scale_variation; double scale; @@ -90,16 +89,16 @@ public: sigc::connection style_set_connection; - static const std::string prefsPath; + static const std::string prefsPath; - virtual void setup(); - virtual void set(const Inkscape::Preferences::Entry& val); - virtual bool root_handler(GdkEvent* event); + virtual void setup(); + virtual void set(const Inkscape::Preferences::Entry& val); + virtual bool root_handler(GdkEvent* event); - virtual const std::string& getPrefsPath(); + virtual const std::string& getPrefsPath(); - void update_cursor(bool /*with_shift*/); + void update_cursor(bool /*with_shift*/); }; } diff --git a/src/ui/tools/text-tool.cpp b/src/ui/tools/text-tool.cpp index a2c0c81ae..1888551cf 100644 --- a/src/ui/tools/text-tool.cpp +++ b/src/ui/tools/text-tool.cpp @@ -1461,7 +1461,7 @@ int TextTool::_styleQueried(SPStyle *style, int property) } sp_text_context_validate_cursor_iterators(this); - GSList *styles_list = NULL; + std::vector<SPItem*> styles_list; Inkscape::Text::Layout::iterator begin_it, end_it; if (this->text_sel_start < this->text_sel_end) { @@ -1477,7 +1477,7 @@ int TextTool::_styleQueried(SPStyle *style, int property) } } for (Inkscape::Text::Layout::iterator it = begin_it ; it < end_it ; it.nextStartOfSpan()) { - SPObject const *pos_obj = 0; + SPObject *pos_obj = 0; void *rawptr = 0; layout->getSourceOfCharacter(it, &rawptr); if (!rawptr || !SP_IS_OBJECT(rawptr)) { @@ -1487,12 +1487,11 @@ int TextTool::_styleQueried(SPStyle *style, int property) while (SP_IS_STRING(pos_obj) && pos_obj->parent) { pos_obj = pos_obj->parent; // SPStrings don't have style } - styles_list = g_slist_prepend(styles_list, (gpointer)pos_obj); + styles_list.insert(styles_list.begin(),(SPItem*)pos_obj); } int result = sp_desktop_query_style_from_list (styles_list, style, property); - g_slist_free(styles_list); return result; } diff --git a/src/ui/tools/tool-base.cpp b/src/ui/tools/tool-base.cpp index a07f2fb86..0f9b3ee7a 100644 --- a/src/ui/tools/tool-base.cpp +++ b/src/ui/tools/tool-base.cpp @@ -20,10 +20,6 @@ #include "widgets/desktop-widget.h" -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include "shortcuts.h" #include "file.h" #include "ui/tools/tool-base.h" @@ -1178,11 +1174,9 @@ SPItem *sp_event_context_find_item(SPDesktop *desktop, Geom::Point const &p, SPItem * sp_event_context_over_item(SPDesktop *desktop, SPItem *item, Geom::Point const &p) { - GSList *temp = NULL; - temp = g_slist_prepend(temp, item); + std::vector<SPItem*> temp; + temp.push_back(item); SPItem *item_at_point = desktop->getItemFromListAtPointBottom(temp, p); - g_slist_free(temp); - return item_at_point; } diff --git a/src/ui/tools/tweak-tool.cpp b/src/ui/tools/tweak-tool.cpp index 80b52fba6..94f7aa135 100644 --- a/src/ui/tools/tweak-tool.cpp +++ b/src/ui/tools/tweak-tool.cpp @@ -153,7 +153,7 @@ void TweakTool::update_cursor (bool with_shift) { gchar *sel_message = NULL; if (!desktop->selection->isEmpty()) { - num = g_slist_length(const_cast<GSList *>(desktop->selection->itemList())); + num = desktop->selection->itemList().size(); sel_message = g_strdup_printf(ngettext("<b>%i</b> object selected","<b>%i</b> objects selected",num), num); } else { sel_message = g_strdup_printf("%s", _("<b>Nothing</b> selected")); @@ -259,8 +259,7 @@ void TweakTool::setup() { { /* TODO: have a look at sp_dyna_draw_context_setup where the same is done.. generalize? at least make it an arcto! */ - Geom::PathVector path; - Geom::Circle(0, 0, 1).getPath(path); + Geom::PathVector path = Geom::Path(Geom::Circle(0,0,1)); SPCurve *c = new SPCurve(path); @@ -372,14 +371,13 @@ sp_tweak_dilate_recursive (Inkscape::Selection *selection, SPItem *item, Geom::P } if (dynamic_cast<SPText *>(item) || dynamic_cast<SPFlowtext *>(item)) { - GSList *items = g_slist_prepend (NULL, item); - GSList *selected = NULL; - GSList *to_select = NULL; + std::vector<SPItem*> items; + items.push_back(item); + std::vector<SPItem*> selected; + std::vector<Inkscape::XML::Node*> to_select; SPDocument *doc = item->document; - sp_item_list_to_curves (items, &selected, &to_select); - g_slist_free (items); - SPObject* newObj = doc->getObjectByRepr(static_cast<Inkscape::XML::Node *>(to_select->data)); - g_slist_free (to_select); + sp_item_list_to_curves (items, selected, to_select); + SPObject* newObj = doc->getObjectByRepr(to_select[0]); item = dynamic_cast<SPItem *>(newObj); g_assert(item != NULL); selection->add(item); @@ -1078,11 +1076,9 @@ sp_tweak_dilate (TweakTool *tc, Geom::Point event_p, Geom::Point p, Geom::Point double move_force = get_move_force(tc); double color_force = MIN(sqrt(path_force)/20.0, 1); - for (GSList *items = g_slist_copy(const_cast<GSList *>(selection->itemList())); - items != NULL; - items = items->next) { - - SPItem *item = dynamic_cast<SPItem *>(static_cast<SPObject *>(items->data)); + std::vector<SPItem*> items=selection->itemList(); + for(std::vector<SPItem*>::const_iterator i=items.begin();i!=items.end();i++){ + SPItem *item = *i; if (is_color_mode (tc->mode)) { if (do_fill || do_stroke || do_opacity) { @@ -1189,7 +1185,7 @@ bool TweakTool::root_handler(GdkEvent* event) { guint num = 0; if (!desktop->selection->isEmpty()) { - num = g_slist_length(const_cast<GSList *>(desktop->selection->itemList())); + num = desktop->selection->itemList().size(); } if (num == 0) { this->message_context->flash(Inkscape::ERROR_MESSAGE, _("<b>Nothing selected!</b> Select objects to tweak.")); diff --git a/src/ui/widget/Makefile_insert b/src/ui/widget/Makefile_insert index e18b790bd..eb98e6872 100644 --- a/src/ui/widget/Makefile_insert +++ b/src/ui/widget/Makefile_insert @@ -6,10 +6,22 @@ ink_common_sources += \ ui/widget/attr-widget.h \ ui/widget/button.h \ ui/widget/button.cpp \ + ui/widget/color-entry.cpp \ + ui/widget/color-entry.h \ + ui/widget/color-icc-selector.cpp \ + ui/widget/color-icc-selector.h \ + ui/widget/color-notebook.cpp \ + ui/widget/color-notebook.h \ + ui/widget/color-wheel-selector.cpp \ + ui/widget/color-wheel-selector.h \ ui/widget/color-picker.cpp \ ui/widget/color-picker.h \ ui/widget/color-preview.cpp \ ui/widget/color-preview.h \ + ui/widget/color-slider.cpp \ + ui/widget/color-slider.h \ + ui/widget/color-scales.cpp \ + ui/widget/color-scales.h \ ui/widget/combo-enums.h \ ui/widget/dock.h \ ui/widget/dock.cpp \ @@ -21,8 +33,10 @@ ink_common_sources += \ ui/widget/entry.h \ ui/widget/filter-effect-chooser.h \ ui/widget/filter-effect-chooser.cpp \ - ui/widget/gimpspinscale.c \ - ui/widget/gimpspinscale.h \ + ui/widget/font-variants.h \ + ui/widget/font-variants.cpp \ + ui/widget/gimpspinscale.c \ + ui/widget/gimpspinscale.h \ ui/widget/gimpcolorwheel.c \ ui/widget/gimpcolorwheel.h \ ui/widget/frame.cpp \ diff --git a/src/ui/widget/addtoicon.cpp b/src/ui/widget/addtoicon.cpp index f15d7abf7..823e24a00 100644 --- a/src/ui/widget/addtoicon.cpp +++ b/src/ui/widget/addtoicon.cpp @@ -12,10 +12,6 @@ # include <config.h> #endif -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -# include <glibmm/threads.h> -#endif - #include "ui/widget/addtoicon.h" #include <gtkmm/icontheme.h> diff --git a/src/ui/widget/button.h b/src/ui/widget/button.h index 471b7d8a2..d3c9afa02 100644 --- a/src/ui/widget/button.h +++ b/src/ui/widget/button.h @@ -10,14 +10,6 @@ #ifndef INKSCAPE_UI_WIDGET_BUTTON_H #define INKSCAPE_UI_WIDGET_BUTTON_H -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif - -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include <gtkmm/checkbutton.h> #include <gtkmm/radiobutton.h> diff --git a/src/ui/widget/clipmaskicon.cpp b/src/ui/widget/clipmaskicon.cpp index 943b1bebc..421f1df1e 100644 --- a/src/ui/widget/clipmaskicon.cpp +++ b/src/ui/widget/clipmaskicon.cpp @@ -11,10 +11,6 @@ # include <config.h> #endif -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -# include <glibmm/threads.h> -#endif - #include "ui/widget/clipmaskicon.h" #include <gtkmm/icontheme.h> diff --git a/src/ui/widget/color-entry.cpp b/src/ui/widget/color-entry.cpp new file mode 100644 index 000000000..f5658c3a6 --- /dev/null +++ b/src/ui/widget/color-entry.cpp @@ -0,0 +1,114 @@ +/** @file + * Entry widget for typing color value in css form + *//* + * Authors: + * Tomasz Boczkowski <penginsbacon@gmail.com> + * + * Copyright (C) 2014 Authors + * Released under GNU GPL, read the file 'COPYING' for more information + */ +#include <glibmm.h> +#include <glibmm/i18n.h> +#include <iomanip> + +#include "color-entry.h" + +namespace Inkscape { +namespace UI { +namespace Widget { + +ColorEntry::ColorEntry(SelectedColor &color) + : _color(color) + , _updating(false) + , _updatingrgba(false) +{ + _color_changed_connection = color.signal_changed.connect(sigc::mem_fun(this, &ColorEntry::_onColorChanged)); + _color_dragged_connection = color.signal_dragged.connect(sigc::mem_fun(this, &ColorEntry::_onColorChanged)); + _onColorChanged(); + + set_max_length(8); + set_width_chars(8); + set_tooltip_text(_("Hexadecimal RGBA value of the color")); +} + +ColorEntry::~ColorEntry() +{ + _color_changed_connection.disconnect(); + _color_dragged_connection.disconnect(); +} + +void ColorEntry::on_changed() +{ + if (_updating) { + return; + } + if (_updatingrgba) { + return; // Typing text into entry box + } + + Glib::ustring text = get_text(); + bool changed = false; + + // Coerce the value format to eight hex digits + if (!text.empty() && text[0] == '#') { + changed = true; + text.erase(0, 1); + if (text.size() == 6) { + // it was a standard RGB hex + unsigned int alpha = SP_COLOR_F_TO_U(_color.alpha()); + text += Glib::ustring::format(std::hex, std::setw(2), std::setfill(L'0'), alpha); + } + } + + gchar *str = g_strdup(text.c_str()); + gchar *end = 0; + guint64 rgba = g_ascii_strtoull(str, &end, 16); + if (end != str) { + ptrdiff_t len = end - str; + if (len < 8) { + rgba = rgba << (4 * (8 - len)); + } + _updatingrgba = true; + if (changed) { + set_text(str); + } + SPColor color(rgba); + _color.setColorAlpha(color, SP_RGBA32_A_F(rgba)); + _updatingrgba = false; + } + g_free(str); +} + + +void ColorEntry::_onColorChanged() +{ + if (_updatingrgba) { + return; + } + + SPColor color = _color.color(); + gdouble alpha = _color.alpha(); + + guint32 rgba = color.toRGBA32(alpha); + Glib::ustring text = Glib::ustring::format(std::hex, std::setw(8), std::setfill(L'0'), rgba); + + Glib::ustring old_text = get_text(); + if (old_text != text) { + _updating = true; + set_text(text); + _updating = 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/ui/widget/color-entry.h b/src/ui/widget/color-entry.h new file mode 100644 index 000000000..edabe1980 --- /dev/null +++ b/src/ui/widget/color-entry.h @@ -0,0 +1,54 @@ +/** @file + * Entry widget for typing color value in css form + *//* + * Authors: + * Tomasz Boczkowski <penginsbacon@gmail.com> + * + * Copyright (C) 2014 Authors + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef SEEN_COLOR_ENTRY_H +#define SEEN_COLOR_ENTRY_H_ + +#include <gtkmm/entry.h> +#include "ui/selected-color.h" + +namespace Inkscape { +namespace UI { +namespace Widget { + +class ColorEntry : public Gtk::Entry +{ +public: + ColorEntry(SelectedColor &color); + virtual ~ColorEntry(); + +protected: + void on_changed(); + +private: + void _onColorChanged(); + + SelectedColor &_color; + sigc::connection _color_changed_connection; + sigc::connection _color_dragged_connection; + bool _updating; + bool _updatingrgba; +}; + +} +} +} + +#endif +/* + 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/ui/widget/color-icc-selector.cpp b/src/ui/widget/color-icc-selector.cpp new file mode 100644 index 000000000..1c31ae33a --- /dev/null +++ b/src/ui/widget/color-icc-selector.cpp @@ -0,0 +1,1079 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <math.h> +#include <gtkmm/adjustment.h> +#include <glibmm/i18n.h> + +#include <gtk/gtk.h> +#include <map> +#include <set> +#include <vector> + +#include "ui/dialog-events.h" +#include "ui/widget/color-icc-selector.h" +#include "ui/widget/color-scales.h" +#include "ui/widget/color-slider.h" +#include "svg/svg-icc-color.h" +#include "colorspace.h" +#include "document.h" +#include "inkscape.h" +#include "profile-manager.h" +#include "widgets/gradient-vector.h" + +#define noDEBUG_LCMS + +#if defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) +#include "color-profile.h" +#include "cms-system.h" +#include "color-profile-cms-fns.h" + +#ifdef DEBUG_LCMS +#include "preferences.h" +#endif // DEBUG_LCMS +#endif // defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) + +#ifdef DEBUG_LCMS +extern guint update_in_progress; +#define DEBUG_MESSAGE(key, ...) \ + { \ + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); \ + bool dump = prefs->getBool("/options/scislac/" #key); \ + bool dumpD = prefs->getBool("/options/scislac/" #key "D"); \ + bool dumpD2 = prefs->getBool("/options/scislac/" #key "D2"); \ + dumpD && = ((update_in_progress == 0) || dumpD2); \ + if (dump) { \ + g_message(__VA_ARGS__); \ + } \ + if (dumpD) { \ + GtkWidget *dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_INFO, \ + GTK_BUTTONS_OK, __VA_ARGS__); \ + g_signal_connect_swapped(dialog, "response", G_CALLBACK(gtk_widget_destroy), dialog); \ + gtk_widget_show_all(dialog); \ + } \ + } +#endif // DEBUG_LCMS + + +#define XPAD 4 +#define YPAD 1 + +namespace { + +size_t maxColorspaceComponentCount = 0; + +#if defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) + +/** + * Internal variable to track all known colorspaces. + */ +std::set<cmsUInt32Number> knownColorspaces; + +#endif + + +/** + * Simple helper to allow bitwise or on GtkAttachOptions. + */ +GtkAttachOptions operator|(GtkAttachOptions lhs, GtkAttachOptions rhs) +{ + return static_cast<GtkAttachOptions>(static_cast<int>(lhs) | static_cast<int>(rhs)); +} + +/** + * Helper function to handle GTK2/GTK3 attachment #ifdef code. + */ +void attachToGridOrTable(GtkWidget *parent, GtkWidget *child, guint left, guint top, guint width, guint height, + bool hexpand = false, bool centered = false, guint xpadding = XPAD, guint ypadding = YPAD) +{ +#if GTK_CHECK_VERSION(3, 0, 0) + #if GTK_CHECK_VERSION(3, 12, 0) + gtk_widget_set_margin_start(child, xpadding); + gtk_widget_set_margin_end(child, xpadding); + #else + gtk_widget_set_margin_left(child, xpadding); + gtk_widget_set_margin_right(child, xpadding); + #endif + + gtk_widget_set_margin_top(child, ypadding); + gtk_widget_set_margin_bottom(child, ypadding); + if (hexpand) { + gtk_widget_set_hexpand(child, TRUE); + } + if (centered) { + gtk_widget_set_halign(child, GTK_ALIGN_CENTER); + gtk_widget_set_valign(child, GTK_ALIGN_CENTER); + } + gtk_grid_attach(GTK_GRID(parent), child, left, top, width, height); +#else + GtkAttachOptions xoptions = + centered ? static_cast<GtkAttachOptions>(0) : hexpand ? (GTK_EXPAND | GTK_FILL) : GTK_FILL; + GtkAttachOptions yoptions = centered ? static_cast<GtkAttachOptions>(0) : GTK_FILL; + + gtk_table_attach(GTK_TABLE(parent), child, left, left + width, top, top + height, xoptions, yoptions, xpadding, + ypadding); +#endif +} + +} // namespace + +/* +icSigRgbData +icSigCmykData +icSigCmyData +*/ +#define SPACE_ID_RGB 0 +#define SPACE_ID_CMY 1 +#define SPACE_ID_CMYK 2 + + +#if defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) +static cmsUInt16Number *getScratch() +{ + // bytes per pixel * input channels * width + static cmsUInt16Number *scritch = static_cast<cmsUInt16Number *>(g_new(cmsUInt16Number, 4 * 1024)); + + return scritch; +} + +colorspace::Component::Component() + : name() + , tip() + , scale(1) +{ +} + +colorspace::Component::Component(std::string const &name, std::string const &tip, guint scale) + : name(name) + , tip(tip) + , scale(scale) +{ +} + +std::vector<colorspace::Component> colorspace::getColorSpaceInfo(uint32_t space) +{ + static std::map<cmsUInt32Number, std::vector<Component> > sets; + if (sets.empty()) { + sets[cmsSigXYZData].push_back(Component("_X", "X", 2)); // TYPE_XYZ_16 + sets[cmsSigXYZData].push_back(Component("_Y", "Y", 1)); + sets[cmsSigXYZData].push_back(Component("_Z", "Z", 2)); + + sets[cmsSigLabData].push_back(Component("_L", "L", 100)); // TYPE_Lab_16 + sets[cmsSigLabData].push_back(Component("_a", "a", 256)); + sets[cmsSigLabData].push_back(Component("_b", "b", 256)); + + // cmsSigLuvData + + sets[cmsSigYCbCrData].push_back(Component("_Y", "Y", 1)); // TYPE_YCbCr_16 + sets[cmsSigYCbCrData].push_back(Component("C_b", "Cb", 1)); + sets[cmsSigYCbCrData].push_back(Component("C_r", "Cr", 1)); + + sets[cmsSigYxyData].push_back(Component("_Y", "Y", 1)); // TYPE_Yxy_16 + sets[cmsSigYxyData].push_back(Component("_x", "x", 1)); + sets[cmsSigYxyData].push_back(Component("y", "y", 1)); + + sets[cmsSigRgbData].push_back(Component(_("_R:"), _("Red"), 1)); // TYPE_RGB_16 + sets[cmsSigRgbData].push_back(Component(_("_G:"), _("Green"), 1)); + sets[cmsSigRgbData].push_back(Component(_("_B:"), _("Blue"), 1)); + + sets[cmsSigGrayData].push_back(Component(_("G:"), _("Gray"), 1)); // TYPE_GRAY_16 + + sets[cmsSigHsvData].push_back(Component(_("_H:"), _("Hue"), 360)); // TYPE_HSV_16 + sets[cmsSigHsvData].push_back(Component(_("_S:"), _("Saturation"), 1)); + sets[cmsSigHsvData].push_back(Component("_V:", "Value", 1)); + + sets[cmsSigHlsData].push_back(Component(_("_H:"), _("Hue"), 360)); // TYPE_HLS_16 + sets[cmsSigHlsData].push_back(Component(_("_L:"), _("Lightness"), 1)); + sets[cmsSigHlsData].push_back(Component(_("_S:"), _("Saturation"), 1)); + + sets[cmsSigCmykData].push_back(Component(_("_C:"), _("Cyan"), 1)); // TYPE_CMYK_16 + sets[cmsSigCmykData].push_back(Component(_("_M:"), _("Magenta"), 1)); + sets[cmsSigCmykData].push_back(Component(_("_Y:"), _("Yellow"), 1)); + sets[cmsSigCmykData].push_back(Component(_("_K:"), _("Black"), 1)); + + sets[cmsSigCmyData].push_back(Component(_("_C:"), _("Cyan"), 1)); // TYPE_CMY_16 + sets[cmsSigCmyData].push_back(Component(_("_M:"), _("Magenta"), 1)); + sets[cmsSigCmyData].push_back(Component(_("_Y:"), _("Yellow"), 1)); + + for (std::map<cmsUInt32Number, std::vector<Component> >::iterator it = sets.begin(); it != sets.end(); ++it) { + knownColorspaces.insert(it->first); + maxColorspaceComponentCount = std::max(maxColorspaceComponentCount, it->second.size()); + } + } + + std::vector<Component> target; + + if (sets.find(space) != sets.end()) { + target = sets[space]; + } + return target; +} + + +std::vector<colorspace::Component> colorspace::getColorSpaceInfo(Inkscape::ColorProfile *prof) +{ + return getColorSpaceInfo(asICColorSpaceSig(prof->getColorSpace())); +} + +#endif // defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) + +namespace Inkscape { +namespace UI { +namespace Widget { + +/** + * Class containing the parts for a single color component's UI presence. + */ +class ComponentUI { + public: + ComponentUI() + : _component() + , _adj(0) + , _slider(0) + , _btn(0) + , _label(0) + , _map(0) + { + } + + ComponentUI(colorspace::Component const &component) + : _component(component) + , _adj(0) + , _slider(0) + , _btn(0) + , _label(0) + , _map(0) + { + } + + colorspace::Component _component; + GtkAdjustment *_adj; // Component adjustment + Inkscape::UI::Widget::ColorSlider *_slider; + GtkWidget *_btn; // spinbutton + GtkWidget *_label; // Label + guchar *_map; +}; + +/** + * Class that implements the internals of the selector. + */ +class ColorICCSelectorImpl { + public: + ColorICCSelectorImpl(ColorICCSelector *owner, SelectedColor &color); + + ~ColorICCSelectorImpl(); + + static void _adjustmentChanged(GtkAdjustment *adjustment, ColorICCSelectorImpl *cs); + + void _sliderGrabbed(); + void _sliderReleased(); + void _sliderChanged(); + + static void _profileSelected(GtkWidget *src, gpointer data); + static void _fixupHit(GtkWidget *src, gpointer data); + +#if defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) + void _setProfile(SVGICCColor *profile); + void _switchToProfile(gchar const *name); +#endif + void _updateSliders(gint ignore); + void _profilesChanged(std::string const &name); + + ColorICCSelector *_owner; + SelectedColor &_color; + + gboolean _updating : 1; + gboolean _dragging : 1; + + guint32 _fixupNeeded; + GtkWidget *_fixupBtn; + GtkWidget *_profileSel; + + std::vector<ComponentUI> _compUI; + + GtkAdjustment *_adj; // Channel adjustment + Inkscape::UI::Widget::ColorSlider *_slider; + GtkWidget *_sbtn; // Spinbutton + GtkWidget *_label; // Label + +#if defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) + std::string _profileName; + Inkscape::ColorProfile *_prof; + guint _profChannelCount; + gulong _profChangedID; +#endif // defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) +}; + + + +const gchar *ColorICCSelector::MODE_NAME = N_("CMS"); + +ColorICCSelector::ColorICCSelector(SelectedColor &color) + : _impl(NULL) +{ + _impl = new ColorICCSelectorImpl(this, color); + init(); + color.signal_changed.connect(sigc::mem_fun(this, &ColorICCSelector::_colorChanged)); + // color.signal_dragged.connect(sigc::mem_fun(this, &ColorICCSelector::_colorChanged)); +} + +ColorICCSelector::~ColorICCSelector() +{ + if (_impl) { + delete _impl; + _impl = 0; + } +} + + + +ColorICCSelectorImpl::ColorICCSelectorImpl(ColorICCSelector *owner, SelectedColor &color) + : _owner(owner) + , _color(color) + , _updating(FALSE) + , _dragging(FALSE) + , _fixupNeeded(0) + , _fixupBtn(0) + , _profileSel(0) + , _compUI() + , _adj(0) + , _slider(0) + , _sbtn(0) + , _label(0) +#if defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) + , _profileName() + , _prof(0) + , _profChannelCount(0) + , _profChangedID(0) +#endif // defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) +{ +} + +ColorICCSelectorImpl::~ColorICCSelectorImpl() +{ + _adj = 0; + _sbtn = 0; + _label = 0; +} + +void ColorICCSelector::init() +{ + gint row = 0; + + _impl->_updating = FALSE; + _impl->_dragging = FALSE; + + GtkWidget *t = GTK_WIDGET(gobj()); + + _impl->_compUI.clear(); + + // Create components + row = 0; + + + _impl->_fixupBtn = gtk_button_new_with_label(_("Fix")); + g_signal_connect(G_OBJECT(_impl->_fixupBtn), "clicked", G_CALLBACK(ColorICCSelectorImpl::_fixupHit), + (gpointer)_impl); + gtk_widget_set_sensitive(_impl->_fixupBtn, FALSE); + gtk_widget_set_tooltip_text(_impl->_fixupBtn, _("Fix RGB fallback to match icc-color() value.")); + // gtk_misc_set_alignment( GTK_MISC (_impl->_fixupBtn), 1.0, 0.5 ); + gtk_widget_show(_impl->_fixupBtn); + + attachToGridOrTable(t, _impl->_fixupBtn, 0, row, 1, 1); + + // Combobox and store with 2 columns : label (0) and full name (1) + GtkListStore *store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING); + _impl->_profileSel = gtk_combo_box_new_with_model(GTK_TREE_MODEL(store)); + + GtkCellRenderer *renderer = gtk_cell_renderer_text_new(); + gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(_impl->_profileSel), renderer, TRUE); + gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(_impl->_profileSel), renderer, "text", 0, NULL); + + GtkTreeIter iter; + gtk_list_store_append(store, &iter); + gtk_list_store_set(store, &iter, 0, _("<none>"), 1, _("<none>"), -1); + + gtk_widget_show(_impl->_profileSel); + gtk_combo_box_set_active(GTK_COMBO_BOX(_impl->_profileSel), 0); + + attachToGridOrTable(t, _impl->_profileSel, 1, row, 1, 1); + +#if defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) + _impl->_profChangedID = g_signal_connect(G_OBJECT(_impl->_profileSel), "changed", + G_CALLBACK(ColorICCSelectorImpl::_profileSelected), (gpointer)_impl); +#else + gtk_widget_set_sensitive(_impl->_profileSel, false); +#endif // defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) + + + row++; + +// populate the data for colorspaces and channels: +#if defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) + std::vector<colorspace::Component> things = colorspace::getColorSpaceInfo(cmsSigRgbData); +#endif // defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) + + for (size_t i = 0; i < maxColorspaceComponentCount; i++) { +#if defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) + if (i < things.size()) { + _impl->_compUI.push_back(ComponentUI(things[i])); + } + else { + _impl->_compUI.push_back(ComponentUI()); + } + + std::string labelStr = (i < things.size()) ? things[i].name.c_str() : ""; +#else + _impl->_compUI.push_back(ComponentUI()); + + std::string labelStr = "."; +#endif + + _impl->_compUI[i]._label = gtk_label_new_with_mnemonic(labelStr.c_str()); + gtk_misc_set_alignment(GTK_MISC(_impl->_compUI[i]._label), 1.0, 0.5); + gtk_widget_show(_impl->_compUI[i]._label); + gtk_widget_set_no_show_all(_impl->_compUI[i]._label, TRUE); + + attachToGridOrTable(t, _impl->_compUI[i]._label, 0, row, 1, 1); + + // Adjustment + guint scaleValue = _impl->_compUI[i]._component.scale; + gdouble step = static_cast<gdouble>(scaleValue) / 100.0; + gdouble page = static_cast<gdouble>(scaleValue) / 10.0; + gint digits = (step > 0.9) ? 0 : 2; + _impl->_compUI[i]._adj = GTK_ADJUSTMENT(gtk_adjustment_new(0.0, 0.0, scaleValue, step, page, page)); + + // Slider + _impl->_compUI[i]._slider = + Gtk::manage(new Inkscape::UI::Widget::ColorSlider(Glib::wrap(_impl->_compUI[i]._adj, true))); +#if defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) + _impl->_compUI[i]._slider->set_tooltip_text((i < things.size()) ? things[i].tip.c_str() : ""); +#else + _impl->_compUI[i]._slider->set_tooltip_text("."); +#endif // defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) + _impl->_compUI[i]._slider->show(); + _impl->_compUI[i]._slider->set_no_show_all(); + + attachToGridOrTable(t, _impl->_compUI[i]._slider->gobj(), 1, row, 1, 1, true); + + _impl->_compUI[i]._btn = gtk_spin_button_new(_impl->_compUI[i]._adj, step, digits); +#if defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) + gtk_widget_set_tooltip_text(_impl->_compUI[i]._btn, (i < things.size()) ? things[i].tip.c_str() : ""); +#else + gtk_widget_set_tooltip_text(_impl->_compUI[i]._btn, "."); +#endif // defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) + sp_dialog_defocus_on_enter(_impl->_compUI[i]._btn); + gtk_label_set_mnemonic_widget(GTK_LABEL(_impl->_compUI[i]._label), _impl->_compUI[i]._btn); + gtk_widget_show(_impl->_compUI[i]._btn); + gtk_widget_set_no_show_all(_impl->_compUI[i]._btn, TRUE); + + attachToGridOrTable(t, _impl->_compUI[i]._btn, 2, row, 1, 1, false, true); + + _impl->_compUI[i]._map = g_new(guchar, 4 * 1024); + memset(_impl->_compUI[i]._map, 0x0ff, 1024 * 4); + + + // Signals + g_signal_connect(G_OBJECT(_impl->_compUI[i]._adj), "value_changed", + G_CALLBACK(ColorICCSelectorImpl::_adjustmentChanged), _impl); + + _impl->_compUI[i]._slider->signal_grabbed.connect(sigc::mem_fun(_impl, &ColorICCSelectorImpl::_sliderGrabbed)); + _impl->_compUI[i]._slider->signal_released.connect( + sigc::mem_fun(_impl, &ColorICCSelectorImpl::_sliderReleased)); + _impl->_compUI[i]._slider->signal_value_changed.connect( + sigc::mem_fun(_impl, &ColorICCSelectorImpl::_sliderChanged)); + + row++; + } + + // Label + _impl->_label = gtk_label_new_with_mnemonic(_("_A:")); + gtk_misc_set_alignment(GTK_MISC(_impl->_label), 1.0, 0.5); + gtk_widget_show(_impl->_label); + + attachToGridOrTable(t, _impl->_label, 0, row, 1, 1); + + // Adjustment + _impl->_adj = GTK_ADJUSTMENT(gtk_adjustment_new(0.0, 0.0, 255.0, 1.0, 10.0, 10.0)); + + // Slider + _impl->_slider = Gtk::manage(new Inkscape::UI::Widget::ColorSlider(Glib::wrap(_impl->_adj, true))); + _impl->_slider->set_tooltip_text(_("Alpha (opacity)")); + _impl->_slider->show(); + + attachToGridOrTable(t, _impl->_slider->gobj(), 1, row, 1, 1, true); + + _impl->_slider->setColors(SP_RGBA32_F_COMPOSE(1.0, 1.0, 1.0, 0.0), SP_RGBA32_F_COMPOSE(1.0, 1.0, 1.0, 0.5), + SP_RGBA32_F_COMPOSE(1.0, 1.0, 1.0, 1.0)); + + + // Spinbutton + _impl->_sbtn = gtk_spin_button_new(GTK_ADJUSTMENT(_impl->_adj), 1.0, 0); + gtk_widget_set_tooltip_text(_impl->_sbtn, _("Alpha (opacity)")); + sp_dialog_defocus_on_enter(_impl->_sbtn); + gtk_label_set_mnemonic_widget(GTK_LABEL(_impl->_label), _impl->_sbtn); + gtk_widget_show(_impl->_sbtn); + + attachToGridOrTable(t, _impl->_sbtn, 2, row, 1, 1, false, true); + + // Signals + g_signal_connect(G_OBJECT(_impl->_adj), "value_changed", G_CALLBACK(ColorICCSelectorImpl::_adjustmentChanged), + _impl); + + _impl->_slider->signal_grabbed.connect(sigc::mem_fun(_impl, &ColorICCSelectorImpl::_sliderGrabbed)); + _impl->_slider->signal_released.connect(sigc::mem_fun(_impl, &ColorICCSelectorImpl::_sliderReleased)); + _impl->_slider->signal_value_changed.connect(sigc::mem_fun(_impl, &ColorICCSelectorImpl::_sliderChanged)); + + gtk_widget_show(t); +} + +void ColorICCSelectorImpl::_fixupHit(GtkWidget * /*src*/, gpointer data) +{ + ColorICCSelectorImpl *self = reinterpret_cast<ColorICCSelectorImpl *>(data); + gtk_widget_set_sensitive(self->_fixupBtn, FALSE); + self->_adjustmentChanged(self->_compUI[0]._adj, self); +} + +#if defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) +void ColorICCSelectorImpl::_profileSelected(GtkWidget * /*src*/, gpointer data) +{ + ColorICCSelectorImpl *self = reinterpret_cast<ColorICCSelectorImpl *>(data); + + GtkTreeIter iter; + if (gtk_combo_box_get_active_iter(GTK_COMBO_BOX(self->_profileSel), &iter)) { + GtkTreeModel *store = gtk_combo_box_get_model(GTK_COMBO_BOX(self->_profileSel)); + gchar *name = 0; + + gtk_tree_model_get(store, &iter, 1, &name, -1); + self->_switchToProfile(name); + gtk_widget_set_tooltip_text(self->_profileSel, name); + + if (name) { + g_free(name); + } + } +} +#endif // defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) + +#if defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) +void ColorICCSelectorImpl::_switchToProfile(gchar const *name) +{ + bool dirty = false; + SPColor tmp(_color.color()); + + if (name) { + if (tmp.icc && tmp.icc->colorProfile == name) { +#ifdef DEBUG_LCMS + g_message("Already at name [%s]", name); +#endif // DEBUG_LCMS + } + else { +#ifdef DEBUG_LCMS + g_message("Need to switch to profile [%s]", name); +#endif // DEBUG_LCMS + if (tmp.icc) { + tmp.icc->colors.clear(); + } + else { + tmp.icc = new SVGICCColor(); + } + tmp.icc->colorProfile = name; + Inkscape::ColorProfile *newProf = SP_ACTIVE_DOCUMENT->profileManager->find(name); + if (newProf) { + cmsHTRANSFORM trans = newProf->getTransfFromSRGB8(); + if (trans) { + guint32 val = _color.color().toRGBA32(0); + guchar pre[4] = { + static_cast<guchar>(SP_RGBA32_R_U(val)), + static_cast<guchar>(SP_RGBA32_G_U(val)), + static_cast<guchar>(SP_RGBA32_B_U(val)), + 255}; +#ifdef DEBUG_LCMS + g_message("Shoving in [%02x] [%02x] [%02x]", pre[0], pre[1], pre[2]); +#endif // DEBUG_LCMS + cmsUInt16Number post[4] = { 0, 0, 0, 0 }; + cmsDoTransform(trans, pre, post, 1); +#ifdef DEBUG_LCMS + g_message("got on out [%04x] [%04x] [%04x] [%04x]", post[0], post[1], post[2], post[3]); +#endif // DEBUG_LCMS +#if HAVE_LIBLCMS1 + guint count = _cmsChannelsOf(asICColorSpaceSig(newProf->getColorSpace())); +#elif HAVE_LIBLCMS2 + guint count = cmsChannelsOf(asICColorSpaceSig(newProf->getColorSpace())); +#endif + + std::vector<colorspace::Component> things = + colorspace::getColorSpaceInfo(asICColorSpaceSig(newProf->getColorSpace())); + + for (guint i = 0; i < count; i++) { + gdouble val = + (((gdouble)post[i]) / 65535.0) * (gdouble)((i < things.size()) ? things[i].scale : 1); +#ifdef DEBUG_LCMS + g_message(" scaled %d by %d to be %f", i, ((i < things.size()) ? things[i].scale : 1), val); +#endif // DEBUG_LCMS + tmp.icc->colors.push_back(val); + } + cmsHTRANSFORM retrans = newProf->getTransfToSRGB8(); + if (retrans) { + cmsDoTransform(retrans, post, pre, 1); +#ifdef DEBUG_LCMS + g_message(" back out [%02x] [%02x] [%02x]", pre[0], pre[1], pre[2]); +#endif // DEBUG_LCMS + tmp.set(SP_RGBA32_U_COMPOSE(pre[0], pre[1], pre[2], 0xff)); + } + } + } + dirty = true; + } + } + else { +#ifdef DEBUG_LCMS + g_message("NUKE THE ICC"); +#endif // DEBUG_LCMS + if (tmp.icc) { + delete tmp.icc; + tmp.icc = 0; + dirty = true; + _fixupHit(0, this); + } + else { +#ifdef DEBUG_LCMS + g_message("No icc to nuke"); +#endif // DEBUG_LCMS + } + } + + if (dirty) { +#ifdef DEBUG_LCMS + g_message("+----------------"); + g_message("+ new color is [%s]", tmp.toString().c_str()); +#endif // DEBUG_LCMS + _setProfile(tmp.icc); + //_adjustmentChanged( _compUI[0]._adj, SP_COLOR_ICC_SELECTOR(_csel) ); + _color.setColor(tmp); +#ifdef DEBUG_LCMS + g_message("+_________________"); +#endif // DEBUG_LCMS + } +} +#endif // defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) + +#if defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) +void ColorICCSelectorImpl::_profilesChanged(std::string const &name) +{ + GtkComboBox *combo = GTK_COMBO_BOX(_profileSel); + + g_signal_handler_block(G_OBJECT(_profileSel), _profChangedID); + + GtkListStore *store = GTK_LIST_STORE(gtk_combo_box_get_model(combo)); + gtk_list_store_clear(store); + + GtkTreeIter iter; + gtk_list_store_append(store, &iter); + gtk_list_store_set(store, &iter, 0, _("<none>"), 1, _("<none>"), -1); + + gtk_combo_box_set_active(combo, 0); + + int index = 1; + const GSList *current = SP_ACTIVE_DOCUMENT->getResourceList("iccprofile"); + while (current) { + SPObject *obj = SP_OBJECT(current->data); + Inkscape::ColorProfile *prof = reinterpret_cast<Inkscape::ColorProfile *>(obj); + + gtk_list_store_append(store, &iter); + gtk_list_store_set(store, &iter, 0, gr_ellipsize_text(prof->name, 25).c_str(), 1, prof->name, -1); + + if (name == prof->name) { + gtk_combo_box_set_active(combo, index); + gtk_widget_set_tooltip_text(_profileSel, prof->name); + } + + index++; + current = g_slist_next(current); + } + + g_signal_handler_unblock(G_OBJECT(_profileSel), _profChangedID); +} +#else +void ColorICCSelectorImpl::_profilesChanged(std::string const & /*name*/) {} +#endif // defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) + +void ColorICCSelector::on_show() +{ +#if GTK_CHECK_VERSION(3, 0, 0) + Gtk::Grid::on_show(); +#else + Gtk::Table::on_show(); +#endif + _colorChanged(); +} + +// Helpers for setting color value + +void ColorICCSelector::_colorChanged() +{ + _impl->_updating = TRUE; +// sp_color_icc_set_color( SP_COLOR_ICC( _icc ), &color ); + +#ifdef DEBUG_LCMS + g_message("/^^^^^^^^^ %p::_colorChanged(%08x:%s)", this, _impl->_color.color().toRGBA32(_impl->_color.alpha()), + ((_impl->_color.color().icc) ? _impl->_color.color().icc->colorProfile.c_str() : "<null>")); +#endif // DEBUG_LCMS + +#ifdef DEBUG_LCMS + g_message("FLIPPIES!!!! %p '%s'", _impl->_color.color().icc, + (_impl->_color.color().icc ? _impl->_color.color().icc->colorProfile.c_str() : "<null>")); +#endif // DEBUG_LCMS + + _impl->_profilesChanged((_impl->_color.color().icc) ? _impl->_color.color().icc->colorProfile : std::string("")); + ColorScales::setScaled(_impl->_adj, _impl->_color.alpha()); + +#if defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) + _impl->_setProfile(_impl->_color.color().icc); + _impl->_fixupNeeded = 0; + gtk_widget_set_sensitive(_impl->_fixupBtn, FALSE); + + if (_impl->_prof) { + if (_impl->_prof->getTransfToSRGB8()) { + cmsUInt16Number tmp[4]; + for (guint i = 0; i < _impl->_profChannelCount; i++) { + gdouble val = 0.0; + if (_impl->_color.color().icc->colors.size() > i) { + if (_impl->_compUI[i]._component.scale == 256) { + val = (_impl->_color.color().icc->colors[i] + 128.0) / + static_cast<gdouble>(_impl->_compUI[i]._component.scale); + } + else { + val = _impl->_color.color().icc->colors[i] / + static_cast<gdouble>(_impl->_compUI[i]._component.scale); + } + } + tmp[i] = val * 0x0ffff; + } + guchar post[4] = { 0, 0, 0, 0 }; + cmsHTRANSFORM trans = _impl->_prof->getTransfToSRGB8(); + if (trans) { + cmsDoTransform(trans, tmp, post, 1); + guint32 other = SP_RGBA32_U_COMPOSE(post[0], post[1], post[2], 255); + if (other != _impl->_color.color().toRGBA32(255)) { + _impl->_fixupNeeded = other; + gtk_widget_set_sensitive(_impl->_fixupBtn, TRUE); +#ifdef DEBUG_LCMS + g_message("Color needs to change 0x%06x to 0x%06x", _color.toRGBA32(255) >> 8, other >> 8); +#endif // DEBUG_LCMS + } + } + } + } +#else +//(void)color; +#endif // defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) + _impl->_updateSliders(-1); + + + _impl->_updating = FALSE; +#ifdef DEBUG_LCMS + g_message("\\_________ %p::_colorChanged()", this); +#endif // DEBUG_LCMS +} + +#if defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) +void ColorICCSelectorImpl::_setProfile(SVGICCColor *profile) +{ +#ifdef DEBUG_LCMS + g_message("/^^^^^^^^^ %p::_setProfile(%s)", this, ((profile) ? profile->colorProfile.c_str() : "<null>")); +#endif // DEBUG_LCMS + bool profChanged = false; + if (_prof && (!profile || (_profileName != profile->colorProfile))) { + // Need to clear out the prior one + profChanged = true; + _profileName.clear(); + _prof = 0; + _profChannelCount = 0; + } + else if (profile && !_prof) { + profChanged = true; + } + + for (size_t i = 0; i < _compUI.size(); i++) { + gtk_widget_hide(_compUI[i]._label); + _compUI[i]._slider->hide(); + gtk_widget_hide(_compUI[i]._btn); + } + + if (profile) { + _prof = SP_ACTIVE_DOCUMENT->profileManager->find(profile->colorProfile.c_str()); + if (_prof && (asICColorProfileClassSig(_prof->getProfileClass()) != cmsSigNamedColorClass)) { +#if HAVE_LIBLCMS1 + _profChannelCount = _cmsChannelsOf(asICColorSpaceSig(_prof->getColorSpace())); +#elif HAVE_LIBLCMS2 + _profChannelCount = cmsChannelsOf(asICColorSpaceSig(_prof->getColorSpace())); +#endif + + if (profChanged) { + std::vector<colorspace::Component> things = + colorspace::getColorSpaceInfo(asICColorSpaceSig(_prof->getColorSpace())); + for (size_t i = 0; (i < things.size()) && (i < _profChannelCount); ++i) { + _compUI[i]._component = things[i]; + } + + for (guint i = 0; i < _profChannelCount; i++) { + gtk_label_set_text_with_mnemonic(GTK_LABEL(_compUI[i]._label), + (i < things.size()) ? things[i].name.c_str() : ""); + + _compUI[i]._slider->set_tooltip_text((i < things.size()) ? things[i].tip.c_str() : ""); + gtk_widget_set_tooltip_text(_compUI[i]._btn, (i < things.size()) ? things[i].tip.c_str() : ""); + + _compUI[i]._slider->setColors(SPColor(0.0, 0.0, 0.0).toRGBA32(0xff), + SPColor(0.5, 0.5, 0.5).toRGBA32(0xff), + SPColor(1.0, 1.0, 1.0).toRGBA32(0xff)); + /* + _compUI[i]._adj = GTK_ADJUSTMENT( gtk_adjustment_new( val, 0.0, _fooScales[i], + step, page, page ) ); + g_signal_connect( G_OBJECT( _compUI[i]._adj ), "value_changed", G_CALLBACK( + _adjustmentChanged ), _csel ); + + sp_color_slider_set_adjustment( SP_COLOR_SLIDER(_compUI[i]._slider), + _compUI[i]._adj ); + gtk_spin_button_set_adjustment( GTK_SPIN_BUTTON(_compUI[i]._btn), + _compUI[i]._adj ); + gtk_spin_button_set_digits( GTK_SPIN_BUTTON(_compUI[i]._btn), digits ); + */ + gtk_widget_show(_compUI[i]._label); + _compUI[i]._slider->show(); + gtk_widget_show(_compUI[i]._btn); + // gtk_adjustment_set_value( _compUI[i]._adj, 0.0 ); + // gtk_adjustment_set_value( _compUI[i]._adj, val ); + } + for (size_t i = _profChannelCount; i < _compUI.size(); i++) { + gtk_widget_hide(_compUI[i]._label); + _compUI[i]._slider->hide(); + gtk_widget_hide(_compUI[i]._btn); + } + } + } + else { + // Give up for now on named colors + _prof = 0; + } + } + +#ifdef DEBUG_LCMS + g_message("\\_________ %p::_setProfile()", this); +#endif // DEBUG_LCMS +} +#endif // defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) + +void ColorICCSelectorImpl::_updateSliders(gint ignore) +{ +#if defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) + if (_color.color().icc) { + for (guint i = 0; i < _profChannelCount; i++) { + gdouble val = 0.0; + if (_color.color().icc->colors.size() > i) { + if (_compUI[i]._component.scale == 256) { + val = (_color.color().icc->colors[i] + 128.0) / static_cast<gdouble>(_compUI[i]._component.scale); + } + else { + val = _color.color().icc->colors[i] / static_cast<gdouble>(_compUI[i]._component.scale); + } + } + gtk_adjustment_set_value(_compUI[i]._adj, val); + } + + if (_prof) { + if (_prof->getTransfToSRGB8()) { + for (guint i = 0; i < _profChannelCount; i++) { + if (static_cast<gint>(i) != ignore) { + cmsUInt16Number *scratch = getScratch(); + cmsUInt16Number filler[4] = { 0, 0, 0, 0 }; + for (guint j = 0; j < _profChannelCount; j++) { + filler[j] = 0x0ffff * ColorScales::getScaled(_compUI[j]._adj); + } + + cmsUInt16Number *p = scratch; + for (guint x = 0; x < 1024; x++) { + for (guint j = 0; j < _profChannelCount; j++) { + if (j == i) { + *p++ = x * 0x0ffff / 1024; + } + else { + *p++ = filler[j]; + } + } + } + + cmsHTRANSFORM trans = _prof->getTransfToSRGB8(); + if (trans) { + cmsDoTransform(trans, scratch, _compUI[i]._map, 1024); + if (_compUI[i]._slider) + { + _compUI[i]._slider->setMap(_compUI[i]._map); + } + } + } + } + } + } + } +#else + (void)ignore; +#endif // defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) + + guint32 start = _color.color().toRGBA32(0x00); + guint32 mid = _color.color().toRGBA32(0x7f); + guint32 end = _color.color().toRGBA32(0xff); + + _slider->setColors(start, mid, end); +} + + +void ColorICCSelectorImpl::_adjustmentChanged(GtkAdjustment *adjustment, ColorICCSelectorImpl *cs) +{ +// // TODO check this. It looks questionable: +// // if a value is entered between 0 and 1 exclusive, normalize it to (int) 0..255 or 0..100 +// if (adjustment->value > 0.0 && adjustment->value < 1.0) { +// gtk_adjustment_set_value( adjustment, floor ((adjustment->value) * adjustment->upper + 0.5) ); +// } + +#ifdef DEBUG_LCMS + g_message("/^^^^^^^^^ %p::_adjustmentChanged()", cs); +#endif // DEBUG_LCMS + + ColorICCSelector *iccSelector = cs->_owner; + if (iccSelector->_impl->_updating) { + return; + } + + iccSelector->_impl->_updating = TRUE; + + gint match = -1; + + SPColor newColor(iccSelector->_impl->_color.color()); + gfloat scaled = ColorScales::getScaled(iccSelector->_impl->_adj); + if (iccSelector->_impl->_adj == adjustment) { +#ifdef DEBUG_LCMS + g_message("ALPHA"); +#endif // DEBUG_LCMS + } + else { +#if defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) + for (size_t i = 0; i < iccSelector->_impl->_compUI.size(); i++) { + if (iccSelector->_impl->_compUI[i]._adj == adjustment) { + match = i; + break; + } + } + if (match >= 0) { +#ifdef DEBUG_LCMS + g_message(" channel %d", match); +#endif // DEBUG_LCMS + } + + + cmsUInt16Number tmp[4]; + for (guint i = 0; i < 4; i++) { + tmp[i] = ColorScales::getScaled(iccSelector->_impl->_compUI[i]._adj) * 0x0ffff; + } + guchar post[4] = { 0, 0, 0, 0 }; + + cmsHTRANSFORM trans = iccSelector->_impl->_prof->getTransfToSRGB8(); + if (trans) { + cmsDoTransform(trans, tmp, post, 1); + } + + SPColor other(SP_RGBA32_U_COMPOSE(post[0], post[1], post[2], 255)); + other.icc = new SVGICCColor(); + if (iccSelector->_impl->_color.color().icc) { + other.icc->colorProfile = iccSelector->_impl->_color.color().icc->colorProfile; + } + + guint32 prior = iccSelector->_impl->_color.color().toRGBA32(255); + guint32 newer = other.toRGBA32(255); + + if (prior != newer) { +#ifdef DEBUG_LCMS + g_message("Transformed color from 0x%08x to 0x%08x", prior, newer); + g_message(" ~~~~ FLIP"); +#endif // DEBUG_LCMS + newColor = other; + newColor.icc->colors.clear(); + for (guint i = 0; i < iccSelector->_impl->_profChannelCount; i++) { + gdouble val = ColorScales::getScaled(iccSelector->_impl->_compUI[i]._adj); + val *= iccSelector->_impl->_compUI[i]._component.scale; + if (iccSelector->_impl->_compUI[i]._component.scale == 256) { + val -= 128; + } + newColor.icc->colors.push_back(val); + } + } +#endif // defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) + } + iccSelector->_impl->_color.setColorAlpha(newColor, scaled); + // iccSelector->_updateInternals( newColor, scaled, iccSelector->_impl->_dragging ); + iccSelector->_impl->_updateSliders(match); + + iccSelector->_impl->_updating = FALSE; +#ifdef DEBUG_LCMS + g_message("\\_________ %p::_adjustmentChanged()", cs); +#endif // DEBUG_LCMS +} + +void ColorICCSelectorImpl::_sliderGrabbed() +{ + // ColorICCSelector* iccSelector = dynamic_cast<ColorICCSelector*>(SP_COLOR_SELECTOR(cs)->base); + // if (!iccSelector->_dragging) { + // iccSelector->_dragging = TRUE; + // iccSelector->_grabbed(); + // iccSelector->_updateInternals( iccSelector->_color, ColorScales::getScaled( iccSelector->_impl->_adj ), + // iccSelector->_dragging ); + // } +} + +void ColorICCSelectorImpl::_sliderReleased() +{ + // ColorICCSelector* iccSelector = dynamic_cast<ColorICCSelector*>(SP_COLOR_SELECTOR(cs)->base); + // if (iccSelector->_dragging) { + // iccSelector->_dragging = FALSE; + // iccSelector->_released(); + // iccSelector->_updateInternals( iccSelector->_color, ColorScales::getScaled( iccSelector->_adj ), + // iccSelector->_dragging ); + // } +} + +#ifdef DEBUG_LCMS +void ColorICCSelectorImpl::_sliderChanged(SPColorSlider *slider, SPColorICCSelector *cs) +#else +void ColorICCSelectorImpl::_sliderChanged() +#endif // DEBUG_LCMS +{ +#ifdef DEBUG_LCMS + g_message("Changed %p and %p", slider, cs); +#endif // DEBUG_LCMS + // ColorICCSelector* iccSelector = dynamic_cast<ColorICCSelector*>(SP_COLOR_SELECTOR(cs)->base); + + // iccSelector->_updateInternals( iccSelector->_color, ColorScales::getScaled( iccSelector->_adj ), + // iccSelector->_dragging ); +} + +Gtk::Widget *ColorICCSelectorFactory::createWidget(Inkscape::UI::SelectedColor &color) const +{ + Gtk::Widget *w = Gtk::manage(new ColorICCSelector(color)); + return w; +} + +Glib::ustring ColorICCSelectorFactory::modeName() const { return gettext(ColorICCSelector::MODE_NAME); } +} +} +} +/* + 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/ui/widget/color-icc-selector.h b/src/ui/widget/color-icc-selector.h new file mode 100644 index 000000000..1bcb0a540 --- /dev/null +++ b/src/ui/widget/color-icc-selector.h @@ -0,0 +1,78 @@ +#ifndef SEEN_SP_COLOR_ICC_SELECTOR_H +#define SEEN_SP_COLOR_ICC_SELECTOR_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <gtkmm/widget.h> +#if WITH_GTKMM_3_0 +#include <gtkmm/grid.h> +#else +#include <gtkmm/table.h> +#endif + +#include "ui/selected-color.h" + +namespace Inkscape { + +class ColorProfile; + +namespace UI { +namespace Widget { + +class ColorICCSelectorImpl; + +class ColorICCSelector +#if GTK_CHECK_VERSION(3, 0, 0) + : public Gtk::Grid +#else + : public Gtk::Table +#endif + { + public: + static const gchar *MODE_NAME; + + ColorICCSelector(SelectedColor &color); + virtual ~ColorICCSelector(); + + virtual void init(); + + protected: + void on_show(); + + virtual void _colorChanged(); + + void _recalcColor(gboolean changing); + + private: + friend class ColorICCSelectorImpl; + + // By default, disallow copy constructor and assignment operator + ColorICCSelector(const ColorICCSelector &obj); + ColorICCSelector &operator=(const ColorICCSelector &obj); + + ColorICCSelectorImpl *_impl; +}; + + +class ColorICCSelectorFactory : public ColorSelectorFactory { + public: + Gtk::Widget *createWidget(SelectedColor &color) const; + Glib::ustring modeName() const; +}; +} +} +} +#endif // SEEN_SP_COLOR_ICC_SELECTOR_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/ui/widget/color-notebook.cpp b/src/ui/widget/color-notebook.cpp new file mode 100644 index 000000000..60abf43bf --- /dev/null +++ b/src/ui/widget/color-notebook.cpp @@ -0,0 +1,379 @@ +/** + * @file + * A notebook with RGB, CMYK, CMS, HSL, and Wheel pages - implementation + */ +/* Authors: + * Lauris Kaplinski <lauris@kaplinski.com> + * bulia byak <buliabyak@users.sf.net> + * Tomasz Boczkowski <penginsbacon@gmail.com> (c++-sification) + * + * Copyright (C) 2001-2014 Authors + * + * This code is in public domain + */ + +#undef SPCS_PREVIEW +#define noDUMP_CHANGE_INFO + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "widgets/icon.h" +#include <cstring> +#include <string> +#include <cstdlib> +#include <cstddef> +#include <glibmm/i18n.h> +#include <gtkmm/label.h> +#include <gtkmm/notebook.h> + +#include "preferences.h" +#include "widgets/spw-utilities.h" +#include "svg/svg-icc-color.h" +#include "inkscape.h" +#include "document.h" +#include "profile-manager.h" +#include "color-profile.h" +#include "cms-system.h" +#include "ui/dialog-events.h" +#include "ui/tools-switch.h" +#include "ui/tools/tool-base.h" +#include "ui/widget/color-entry.h" +#include "ui/widget/color-icc-selector.h" +#include "ui/widget/color-notebook.h" +#include "ui/widget/color-scales.h" +#include "ui/widget/color-wheel-selector.h" + +using Inkscape::CMSSystem; + +#define XPAD 4 +#define YPAD 1 + +namespace Inkscape { +namespace UI { +namespace Widget { + + +ColorNotebook::ColorNotebook(SelectedColor &color) +#if GTK_CHECK_VERSION(3, 0, 0) + : Gtk::Grid() +#else + : Gtk::Table(2, 3, false) +#endif + , _selected_color(color) + +{ + Page *page; + + page = new Page(new ColorScalesFactory(SP_COLOR_SCALES_MODE_RGB), true); + _available_pages.push_back(page); + page = new Page(new ColorScalesFactory(SP_COLOR_SCALES_MODE_HSV), true); + _available_pages.push_back(page); + page = new Page(new ColorScalesFactory(SP_COLOR_SCALES_MODE_CMYK), true); + _available_pages.push_back(page); + page = new Page(new ColorWheelSelectorFactory, true); + _available_pages.push_back(page); +#if defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) + page = new Page(new ColorICCSelectorFactory, true); + _available_pages.push_back(page); +#endif + + _initUI(); + + _selected_color.signal_changed.connect(sigc::mem_fun(this, &ColorNotebook::_onSelectedColorChanged)); + _selected_color.signal_dragged.connect(sigc::mem_fun(this, &ColorNotebook::_onSelectedColorChanged)); +} + +ColorNotebook::~ColorNotebook() +{ + if (_buttons) { + delete[] _buttons; + _buttons = 0; + } +} + +ColorNotebook::Page::Page(Inkscape::UI::ColorSelectorFactory *selector_factory, bool enabled_full) + : selector_factory(selector_factory) + , enabled_full(enabled_full) +{ +} + + +void ColorNotebook::_initUI() +{ + guint row = 0; + + Gtk::Notebook *notebook = Gtk::manage(new Gtk::Notebook); + notebook->show(); + notebook->set_show_border(false); + notebook->set_show_tabs(false); + _book = GTK_WIDGET(notebook->gobj()); + +#if GTK_CHECK_VERSION(3, 0, 0) + _buttonbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 2); + gtk_box_set_homogeneous(GTK_BOX(_buttonbox), TRUE); +#else + _buttonbox = gtk_hbox_new(TRUE, 2); +#endif + + gtk_widget_show(_buttonbox); + _buttons = new GtkWidget *[_available_pages.size()]; + + for (int i = 0; static_cast<size_t>(i) < _available_pages.size(); i++) { + _addPage(_available_pages[i]); + } + + sp_set_font_size_smaller(_buttonbox); + +#if GTK_CHECK_VERSION(3, 0, 0) + #if GTK_CHECK_VERSION(3, 12, 0) + gtk_widget_set_margin_start(_buttonbox, XPAD); + gtk_widget_set_margin_end(_buttonbox, XPAD); + #else + gtk_widget_set_margin_left(_buttonbox, XPAD); + gtk_widget_set_margin_right(_buttonbox, XPAD); + #endif + gtk_widget_set_margin_top(_buttonbox, YPAD); + gtk_widget_set_margin_bottom(_buttonbox, YPAD); + gtk_widget_set_hexpand(_buttonbox, TRUE); + gtk_widget_set_valign(_buttonbox, GTK_ALIGN_CENTER); + attach(*Glib::wrap(_buttonbox), 0, row, 2, 1); +#else + attach(*Glib::wrap(_buttonbox), 0, 2, row, row + 1, Gtk::EXPAND | Gtk::FILL, static_cast<Gtk::AttachOptions>(0), + XPAD, YPAD); +#endif + + row++; + +#if GTK_CHECK_VERSION(3, 0, 0) +#if GTK_CHECK_VERSION(3, 12, 0) + gtk_widget_set_margin_start(_book, XPAD * 2); + gtk_widget_set_margin_end(_book, XPAD * 2); +#else + gtk_widget_set_margin_left(_book, XPAD * 2); + gtk_widget_set_margin_right(_book, XPAD * 2); +#endif + gtk_widget_set_margin_top(_book, YPAD); + gtk_widget_set_margin_bottom(_book, YPAD); + gtk_widget_set_hexpand(_book, TRUE); + gtk_widget_set_vexpand(_book, TRUE); + attach(*notebook, 0, row, 2, 1); +#else + attach(*notebook, 0, 2, row, row + 1, Gtk::EXPAND | Gtk::FILL, Gtk::EXPAND | Gtk::FILL, XPAD * 2, YPAD); +#endif + + // restore the last active page + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + _setCurrentPage(prefs->getInt("/colorselector/page", 0)); + row++; + +#if GTK_CHECK_VERSION(3, 0, 0) + GtkWidget *rgbabox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); +#else + GtkWidget *rgbabox = gtk_hbox_new(FALSE, 0); +#endif + +#if defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) + /* Create color management icons */ + _box_colormanaged = gtk_event_box_new(); + GtkWidget *colormanaged = gtk_image_new_from_icon_name("color-management-icon", GTK_ICON_SIZE_SMALL_TOOLBAR); + gtk_container_add(GTK_CONTAINER(_box_colormanaged), colormanaged); + gtk_widget_set_tooltip_text(_box_colormanaged, _("Color Managed")); + gtk_widget_set_sensitive(_box_colormanaged, false); + gtk_box_pack_start(GTK_BOX(rgbabox), _box_colormanaged, FALSE, FALSE, 2); + + _box_outofgamut = gtk_event_box_new(); + GtkWidget *outofgamut = gtk_image_new_from_icon_name("out-of-gamut-icon", GTK_ICON_SIZE_SMALL_TOOLBAR); + gtk_container_add(GTK_CONTAINER(_box_outofgamut), outofgamut); + gtk_widget_set_tooltip_text(_box_outofgamut, _("Out of gamut!")); + gtk_widget_set_sensitive(_box_outofgamut, false); + gtk_box_pack_start(GTK_BOX(rgbabox), _box_outofgamut, FALSE, FALSE, 2); + + _box_toomuchink = gtk_event_box_new(); + GtkWidget *toomuchink = gtk_image_new_from_icon_name("too-much-ink-icon", GTK_ICON_SIZE_SMALL_TOOLBAR); + gtk_container_add(GTK_CONTAINER(_box_toomuchink), toomuchink); + gtk_widget_set_tooltip_text(_box_toomuchink, _("Too much ink!")); + gtk_widget_set_sensitive(_box_toomuchink, false); + gtk_box_pack_start(GTK_BOX(rgbabox), _box_toomuchink, FALSE, FALSE, 2); +#endif // defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) + + + /* Color picker */ + GtkWidget *picker = gtk_image_new_from_icon_name("color-picker", GTK_ICON_SIZE_SMALL_TOOLBAR); + _btn_picker = gtk_button_new(); + gtk_button_set_relief(GTK_BUTTON(_btn_picker), GTK_RELIEF_NONE); + gtk_container_add(GTK_CONTAINER(_btn_picker), picker); + gtk_widget_set_tooltip_text(_btn_picker, _("Pick colors from image")); + gtk_box_pack_start(GTK_BOX(rgbabox), _btn_picker, FALSE, FALSE, 2); + g_signal_connect(G_OBJECT(_btn_picker), "clicked", G_CALLBACK(ColorNotebook::_onPickerClicked), this); + + /* Create RGBA entry and color preview */ + _rgbal = gtk_label_new_with_mnemonic(_("RGBA_:")); + gtk_misc_set_alignment(GTK_MISC(_rgbal), 1.0, 0.5); + gtk_box_pack_start(GTK_BOX(rgbabox), _rgbal, TRUE, TRUE, 2); + + ColorEntry *rgba_entry = Gtk::manage(new ColorEntry(_selected_color)); + sp_dialog_defocus_on_enter(GTK_WIDGET(rgba_entry->gobj())); + gtk_box_pack_start(GTK_BOX(rgbabox), GTK_WIDGET(rgba_entry->gobj()), FALSE, FALSE, 0); + gtk_label_set_mnemonic_widget(GTK_LABEL(_rgbal), GTK_WIDGET(rgba_entry->gobj())); + + sp_set_font_size_smaller(rgbabox); + gtk_widget_show_all(rgbabox); + +#if defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) + // the "too much ink" icon is initially hidden + gtk_widget_hide(GTK_WIDGET(_box_toomuchink)); +#endif // defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) + +#if GTK_CHECK_VERSION(3, 0, 0) + #if GTK_CHECK_VERSION(3, 12, 0) + gtk_widget_set_margin_start(rgbabox, XPAD); + gtk_widget_set_margin_end(rgbabox, XPAD); + #else + gtk_widget_set_margin_left(rgbabox, XPAD); + gtk_widget_set_margin_right(rgbabox, XPAD); + #endif + gtk_widget_set_margin_top(rgbabox, YPAD); + gtk_widget_set_margin_bottom(rgbabox, YPAD); + attach(*Glib::wrap(rgbabox), 0, row, 2, 1); +#else + attach(*Glib::wrap(rgbabox), 0, 2, row, row + 1, Gtk::FILL, Gtk::SHRINK, XPAD, YPAD); +#endif + +#ifdef SPCS_PREVIEW + _p = sp_color_preview_new(0xffffffff); + gtk_widget_show(_p); + attach(*Glib::wrap(_p), 2, 3, row, row + 1, Gtk::FILL, Gtk::FILL, XPAD, YPAD); +#endif + + g_signal_connect(G_OBJECT(_book), "switch-page", G_CALLBACK(ColorNotebook::_onPageSwitched), this); +} + +void ColorNotebook::_onPickerClicked(GtkWidget * /*widget*/, ColorNotebook * /*colorbook*/) +{ + // Set the dropper into a "one click" mode, so it reverts to the previous tool after a click + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + prefs->setBool("/tools/dropper/onetimepick", true); + Inkscape::UI::Tools::sp_toggle_dropper(SP_ACTIVE_DESKTOP); +} + +void ColorNotebook::_onButtonClicked(GtkWidget *widget, ColorNotebook *nb) +{ + if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) { + return; + } + + for (gint i = 0; i < gtk_notebook_get_n_pages(GTK_NOTEBOOK(nb->_book)); i++) { + if (nb->_buttons[i] == widget) { + gtk_notebook_set_current_page(GTK_NOTEBOOK(nb->_book), i); + } + } +} + +void ColorNotebook::_onSelectedColorChanged() { _updateICCButtons(); } + +void ColorNotebook::_onPageSwitched(GtkNotebook *notebook, GtkWidget *page, guint page_num, ColorNotebook *colorbook) +{ + if (colorbook->get_visible()) { + // remember the page we switched to + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + prefs->setInt("/colorselector/page", page_num); + } +} + + +// TODO pass in param so as to avoid the need for SP_ACTIVE_DOCUMENT +void ColorNotebook::_updateICCButtons() +{ + SPColor color = _selected_color.color(); + gfloat alpha = _selected_color.alpha(); + + g_return_if_fail((0.0 <= alpha) && (alpha <= 1.0)); + +#if defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) + /* update color management icon*/ + gtk_widget_set_sensitive(_box_colormanaged, color.icc != NULL); + + /* update out-of-gamut icon */ + gtk_widget_set_sensitive(_box_outofgamut, false); + if (color.icc) { + Inkscape::ColorProfile *target_profile = + SP_ACTIVE_DOCUMENT->profileManager->find(color.icc->colorProfile.c_str()); + if (target_profile) + gtk_widget_set_sensitive(_box_outofgamut, target_profile->GamutCheck(color)); + } + + /* update too-much-ink icon */ + gtk_widget_set_sensitive(_box_toomuchink, false); + if (color.icc) { + Inkscape::ColorProfile *prof = SP_ACTIVE_DOCUMENT->profileManager->find(color.icc->colorProfile.c_str()); + if (prof && CMSSystem::isPrintColorSpace(prof)) { + gtk_widget_show(GTK_WIDGET(_box_toomuchink)); + double ink_sum = 0; + for (unsigned int i = 0; i < color.icc->colors.size(); i++) { + ink_sum += color.icc->colors[i]; + } + + /* Some literature states that when the sum of paint values exceed 320%, it is considered to be a satured + color, + which means the paper can get too wet due to an excessive ammount of ink. This may lead to several + issues + such as misalignment and poor quality of printing in general.*/ + if (ink_sum > 3.2) + gtk_widget_set_sensitive(_box_toomuchink, true); + } + else { + gtk_widget_hide(GTK_WIDGET(_box_toomuchink)); + } + } +#endif // defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) +} + +void ColorNotebook::_setCurrentPage(int i) +{ + gtk_notebook_set_current_page(GTK_NOTEBOOK(_book), i); + + if (_buttons && (static_cast<size_t>(i) < _available_pages.size())) { + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(_buttons[i]), TRUE); + } +} + +void ColorNotebook::_addPage(Page &page) +{ + Gtk::Widget *selector_widget; + + selector_widget = page.selector_factory->createWidget(_selected_color); + if (selector_widget) { + selector_widget->show(); + + Glib::ustring mode_name = page.selector_factory->modeName(); + Gtk::Widget *tab_label = Gtk::manage(new Gtk::Label(mode_name)); + gint page_num = gtk_notebook_append_page(GTK_NOTEBOOK(_book), selector_widget->gobj(), tab_label->gobj()); + + _buttons[page_num] = gtk_radio_button_new_with_label(NULL, mode_name.c_str()); + gtk_toggle_button_set_mode(GTK_TOGGLE_BUTTON(_buttons[page_num]), FALSE); + if (page_num > 0) { + GSList *group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(_buttons[0])); + gtk_radio_button_set_group(GTK_RADIO_BUTTON(_buttons[page_num]), group); + } + gtk_widget_show(_buttons[page_num]); + gtk_box_pack_start(GTK_BOX(_buttonbox), _buttons[page_num], TRUE, TRUE, 0); + + g_signal_connect(G_OBJECT(_buttons[page_num]), "clicked", G_CALLBACK(_onButtonClicked), this); + } +} +} +} +} + +/* + 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/ui/widget/color-notebook.h b/src/ui/widget/color-notebook.h new file mode 100644 index 000000000..d28028c72 --- /dev/null +++ b/src/ui/widget/color-notebook.h @@ -0,0 +1,99 @@ +/** + * @file + * A notebook with RGB, CMYK, CMS, HSL, and Wheel pages + */ +/* Authors: + * Lauris Kaplinski <lauris@kaplinski.com> + * bulia byak <buliabyak@users.sf.net> + * Tomasz Boczkowski <penginsbacon@gmail.com> (c++-sification) + * + * Copyright (C) 2001-2014 Authors + * + * This code is in public domain + */ +#ifndef SEEN_SP_COLOR_NOTEBOOK_H +#define SEEN_SP_COLOR_NOTEBOOK_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <boost/ptr_container/ptr_vector.hpp> +#if WITH_GTKMM_3_0 +#include <gtkmm/grid.h> +#else +#include <gtkmm/table.h> +#endif +#include <gtk/gtk.h> +#include <glib.h> + +#include "color.h" +#include "ui/selected-color.h" + +namespace Inkscape { +namespace UI { +namespace Widget { + +class ColorNotebook +#if GTK_CHECK_VERSION(3, 0, 0) + : public Gtk::Grid +#else + : public Gtk::Table +#endif +{ +public: + ColorNotebook(SelectedColor &color); + virtual ~ColorNotebook(); + +protected: + struct Page { + Page(Inkscape::UI::ColorSelectorFactory *selector_factory, bool enabled_full); + + Inkscape::UI::ColorSelectorFactory *selector_factory; + bool enabled_full; + }; + + virtual void _initUI(); + void _addPage(Page &page); + + static void _onButtonClicked(GtkWidget *widget, ColorNotebook *colorbook); + static void _onPickerClicked(GtkWidget *widget, ColorNotebook *colorbook); + static void _onPageSwitched(GtkNotebook *notebook, GtkWidget *page, guint page_num, ColorNotebook *colorbook); + virtual void _onSelectedColorChanged(); + + void _updateICCButtons(); + void _setCurrentPage(int i); + + Inkscape::UI::SelectedColor &_selected_color; + gulong _entryId; + GtkWidget *_book; + GtkWidget *_buttonbox; + GtkWidget **_buttons; + GtkWidget *_rgbal; /* RGBA entry */ +#if defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) + GtkWidget *_box_outofgamut, *_box_colormanaged, *_box_toomuchink; +#endif // defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) + GtkWidget *_btn_picker; + GtkWidget *_p; /* Color preview */ + boost::ptr_vector<Page> _available_pages; + +private: + // By default, disallow copy constructor and assignment operator + ColorNotebook(const ColorNotebook &obj); + ColorNotebook &operator=(const ColorNotebook &obj); +}; +} +} +} +#endif // SEEN_SP_COLOR_NOTEBOOK_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/ui/widget/color-picker.cpp b/src/ui/widget/color-picker.cpp index d4c4d394e..a66fbfc9c 100644 --- a/src/ui/widget/color-picker.cpp +++ b/src/ui/widget/color-picker.cpp @@ -17,7 +17,7 @@ #include "document-undo.h" #include "ui/dialog-events.h" -#include "widgets/sp-color-notebook.h" +#include "ui/widget/color-notebook.h" #include "verbs.h" @@ -27,8 +27,6 @@ namespace Inkscape { namespace UI { namespace Widget { -void sp_color_picker_color_mod(SPColorSelector *csel, GObject *cp); - ColorPicker::ColorPicker (const Glib::ustring& title, const Glib::ustring& tip, guint32 rgba, bool undo) : _preview(rgba), _title(title), _rgba(rgba), _undo(undo), @@ -39,12 +37,15 @@ ColorPicker::ColorPicker (const Glib::ustring& title, const Glib::ustring& tip, _preview.show(); add (_preview); set_tooltip_text (tip); + + _selected_color.signal_changed.connect(sigc::mem_fun(this, &ColorPicker::_onSelectedColorChanged)); + _selected_color.signal_dragged.connect(sigc::mem_fun(this, &ColorPicker::_onSelectedColorChanged)); + _selected_color.signal_released.connect(sigc::mem_fun(this, &ColorPicker::_onSelectedColorChanged)); } ColorPicker::~ColorPicker() { closeWindow(); - _colorSelector = NULL; } void ColorPicker::setupDialog(const Glib::ustring &title) @@ -55,25 +56,17 @@ void ColorPicker::setupDialog(const Glib::ustring &title) _colorSelectorDialog.hide(); _colorSelectorDialog.set_title (title); _colorSelectorDialog.set_border_width (4); - _colorSelector = SP_COLOR_SELECTOR(sp_color_selector_new(SP_TYPE_COLOR_NOTEBOOK)); + + _color_selector = Gtk::manage(new ColorNotebook(_selected_color)); #if WITH_GTKMM_3_0 _colorSelectorDialog.get_content_area()->pack_start ( - *Glib::wrap(&_colorSelector->vbox), true, true, 0); + *_color_selector, true, true, 0); #else _colorSelectorDialog.get_vbox()->pack_start ( - *Glib::wrap(&_colorSelector->vbox), true, true, 0); + *_color_selector, true, true, 0); #endif - - g_signal_connect(G_OBJECT(_colorSelector), "dragged", - G_CALLBACK(sp_color_picker_color_mod), (void *)this); - g_signal_connect(G_OBJECT(_colorSelector), "released", - G_CALLBACK(sp_color_picker_color_mod), (void *)this); - g_signal_connect(G_OBJECT(_colorSelector), "changed", - G_CALLBACK(sp_color_picker_color_mod), (void *)this); - - gtk_widget_show(GTK_WIDGET(_colorSelector)); - + _color_selector->show(); } void ColorPicker::setRgba32 (guint32 rgba) @@ -82,11 +75,11 @@ void ColorPicker::setRgba32 (guint32 rgba) _preview.setRgba32 (rgba); _rgba = rgba; - if (_colorSelector) + if (_color_selector) { - SPColor color; - color.set( rgba ); - _colorSelector->base->setColorAlpha(color, SP_RGBA32_A_F(rgba)); + _updating = true; + _selected_color.setValue(rgba); + _updating = false; } } @@ -97,11 +90,11 @@ void ColorPicker::closeWindow() void ColorPicker::on_clicked() { - if (_colorSelector) + if (_color_selector) { - SPColor color; - color.set( _rgba ); - _colorSelector->base->setColorAlpha(color, SP_RGBA32_A_F(_rgba)); + _updating = true; + _selected_color.setValue(_rgba); + _updating = false; } _colorSelectorDialog.show(); } @@ -110,34 +103,31 @@ void ColorPicker::on_changed (guint32) { } -void sp_color_picker_color_mod(SPColorSelector *csel, GObject *cp) -{ +void ColorPicker::_onSelectedColorChanged() { + if (_updating) { + return; + } + if (_in_use) { return; } else { _in_use = true; } - SPColor color; - float alpha = 0; - csel->base->getColorAlpha(color, alpha); - guint32 rgba = color.toRGBA32( alpha ); - - ColorPicker *ptr = reinterpret_cast<ColorPicker *>(cp); - - (ptr->_preview).setRgba32 (rgba); + guint32 rgba = _selected_color.value(); + _preview.setRgba32(rgba); - if (ptr->_undo && SP_ACTIVE_DESKTOP) + if (_undo && SP_ACTIVE_DESKTOP) { DocumentUndo::done(SP_ACTIVE_DESKTOP->getDocument(), SP_VERB_NONE, /* TODO: annotate */ "color-picker.cpp:130"); + } - ptr->on_changed (rgba); + on_changed(rgba); _in_use = false; - ptr->_changed_signal.emit (rgba); - ptr->_rgba = rgba; + _changed_signal.emit(rgba); + _rgba = rgba; } - }//namespace Widget }//namespace UI }//namespace Inkscape diff --git a/src/ui/widget/color-picker.h b/src/ui/widget/color-picker.h index b4da5dbf2..e8a738b5b 100644 --- a/src/ui/widget/color-picker.h +++ b/src/ui/widget/color-picker.h @@ -13,19 +13,12 @@ #ifndef __COLOR_PICKER_H__ #define __COLOR_PICKER_H__ -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif - #include <stddef.h> -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include <gtkmm/dialog.h> #include <gtkmm/button.h> #include <sigc++/sigc++.h> +#include "ui/selected-color.h" #include "ui/widget/color-preview.h" struct SPColorSelector; @@ -57,7 +50,7 @@ public: protected: - friend void sp_color_picker_color_mod(SPColorSelector *csel, GObject *cp); + void _onSelectedColorChanged(); virtual void on_clicked(); virtual void on_changed (guint32); @@ -67,13 +60,14 @@ protected: sigc::signal<void,guint32> _changed_signal; guint32 _rgba; bool _undo; - + bool _updating; //Dialog void setupDialog(const Glib::ustring &title); //Inkscape::UI::Dialog::Dialog _colorSelectorDialog; Gtk::Dialog _colorSelectorDialog; - SPColorSelector *_colorSelector; + SelectedColor _selected_color; + Gtk::Widget *_color_selector; }; }//namespace Widget diff --git a/src/ui/widget/color-preview.h b/src/ui/widget/color-preview.h index 959b5e09b..caddfb9a2 100644 --- a/src/ui/widget/color-preview.h +++ b/src/ui/widget/color-preview.h @@ -15,10 +15,6 @@ # include <config.h> #endif -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include <gtkmm/widget.h> namespace Inkscape { diff --git a/src/ui/widget/color-scales.cpp b/src/ui/widget/color-scales.cpp new file mode 100644 index 000000000..170f83887 --- /dev/null +++ b/src/ui/widget/color-scales.cpp @@ -0,0 +1,677 @@ +/* + * bulia byak <buliabyak@users.sf.net> + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <math.h> +#include <gtkmm/adjustment.h> +#include <glibmm/i18n.h> +#include <gtk/gtk.h> + +#include "svg/svg-icc-color.h" +#include "ui/dialog-events.h" +#include "ui/widget/color-scales.h" +#include "ui/widget/color-slider.h" + +#define CSC_CHANNEL_R (1 << 0) +#define CSC_CHANNEL_G (1 << 1) +#define CSC_CHANNEL_B (1 << 2) +#define CSC_CHANNEL_A (1 << 3) +#define CSC_CHANNEL_H (1 << 0) +#define CSC_CHANNEL_S (1 << 1) +#define CSC_CHANNEL_V (1 << 2) +#define CSC_CHANNEL_C (1 << 0) +#define CSC_CHANNEL_M (1 << 1) +#define CSC_CHANNEL_Y (1 << 2) +#define CSC_CHANNEL_K (1 << 3) +#define CSC_CHANNEL_CMYKA (1 << 4) + +#define CSC_CHANNELS_ALL 0 + +#define XPAD 4 +#define YPAD 1 + +#define noDUMP_CHANGE_INFO 1 + +namespace Inkscape { +namespace UI { +namespace Widget { + + +static const gchar *sp_color_scales_hue_map(); + +const gchar *ColorScales::SUBMODE_NAMES[] = { N_("None"), N_("RGB"), N_("HSL"), N_("CMYK") }; + +ColorScales::ColorScales(SelectedColor &color, SPColorScalesMode mode) +#if GTK_CHECK_VERSION(3, 0, 0) + : Gtk::Grid() +#else + : Gtk::Table(5, 3, false) +#endif + , _color(color) + , _rangeLimit(255.0) + , _updating(FALSE) + , _dragging(FALSE) +{ + for (gint i = 0; i < 5; i++) { + _l[i] = 0; + _a[i] = 0; + _s[i] = 0; + _b[i] = 0; + } + + _initUI(mode); + + _color.signal_changed.connect(sigc::mem_fun(this, &ColorScales::_onColorChanged)); + _color.signal_dragged.connect(sigc::mem_fun(this, &ColorScales::_onColorChanged)); +} + +ColorScales::~ColorScales() +{ + for (gint i = 0; i < 5; i++) { + _l[i] = 0; + _a[i] = 0; + _s[i] = 0; + _b[i] = 0; + } +} + +void ColorScales::_initUI(SPColorScalesMode mode) +{ + gint i; + + _updating = FALSE; + _dragging = FALSE; + + GtkWidget *t = GTK_WIDGET(gobj()); + + /* Create components */ + for (i = 0; i < static_cast<gint>(G_N_ELEMENTS(_a)); i++) { + /* Label */ + _l[i] = gtk_label_new(""); + gtk_misc_set_alignment(GTK_MISC(_l[i]), 1.0, 0.5); + gtk_widget_show(_l[i]); + +#if GTK_CHECK_VERSION(3, 0, 0) + #if GTK_CHECK_VERSION(3, 12, 0) + gtk_widget_set_margin_start(_l[i], XPAD); + gtk_widget_set_margin_end(_l[i], XPAD); + #else + gtk_widget_set_margin_left(_l[i], XPAD); + gtk_widget_set_margin_right(_l[i], XPAD); + #endif + gtk_widget_set_margin_top(_l[i], YPAD); + gtk_widget_set_margin_bottom(_l[i], YPAD); + gtk_grid_attach(GTK_GRID(t), _l[i], 0, i, 1, 1); +#else + gtk_table_attach(GTK_TABLE(t), _l[i], 0, 1, i, i + 1, GTK_FILL, GTK_FILL, XPAD, YPAD); +#endif + + /* Adjustment */ + _a[i] = GTK_ADJUSTMENT(gtk_adjustment_new(0.0, 0.0, _rangeLimit, 1.0, 10.0, 10.0)); + /* Slider */ + _s[i] = Gtk::manage(new Inkscape::UI::Widget::ColorSlider(Glib::wrap(_a[i], true))); + _s[i]->show(); + +#if GTK_CHECK_VERSION(3, 0, 0) + #if GTK_CHECK_VERSION(3, 12, 0) + _s[i]->set_margin_start(XPAD); + _s[i]->set_margin_end(XPAD); + #else + _s[i]->set_margin_left(XPAD); + _s[i]->set_margin_right(XPAD); + #endif + _s[i]->set_margin_top(YPAD); + _s[i]->set_margin_bottom(YPAD); + _s[i]->set_hexpand(true); + gtk_grid_attach(GTK_GRID(t), _s[i]->gobj(), 1, i, 1, 1); +#else + gtk_table_attach(GTK_TABLE(t), _s[i]->gobj(), 1, 2, i, i + 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), + GTK_FILL, XPAD, YPAD); +#endif + + /* Spinbutton */ + _b[i] = gtk_spin_button_new(GTK_ADJUSTMENT(_a[i]), 1.0, 0); + sp_dialog_defocus_on_enter(_b[i]); + gtk_label_set_mnemonic_widget(GTK_LABEL(_l[i]), _b[i]); + gtk_widget_show(_b[i]); + +#if GTK_CHECK_VERSION(3, 0, 0) + #if GTK_CHECK_VERSION(3, 12, 0) + gtk_widget_set_margin_start(_b[i], XPAD); + gtk_widget_set_margin_end(_b[i], XPAD); + #else + gtk_widget_set_margin_left(_b[i], XPAD); + gtk_widget_set_margin_right(_b[i], XPAD); + #endif + gtk_widget_set_margin_top(_b[i], YPAD); + gtk_widget_set_margin_bottom(_b[i], YPAD); + gtk_widget_set_halign(_b[i], GTK_ALIGN_CENTER); + gtk_widget_set_valign(_b[i], GTK_ALIGN_CENTER); + gtk_grid_attach(GTK_GRID(t), _b[i], 2, i, 1, 1); +#else + gtk_table_attach(GTK_TABLE(t), _b[i], 2, 3, i, i + 1, (GtkAttachOptions)0, (GtkAttachOptions)0, XPAD, YPAD); +#endif + + /* Attach channel value to adjustment */ + g_object_set_data(G_OBJECT(_a[i]), "channel", GINT_TO_POINTER(i)); + /* Signals */ + g_signal_connect(G_OBJECT(_a[i]), "value_changed", G_CALLBACK(_adjustmentAnyChanged), this); + _s[i]->signal_grabbed.connect(sigc::mem_fun(this, &ColorScales::_sliderAnyGrabbed)); + _s[i]->signal_released.connect(sigc::mem_fun(this, &ColorScales::_sliderAnyReleased)); + _s[i]->signal_value_changed.connect(sigc::mem_fun(this, &ColorScales::_sliderAnyChanged)); + } + + //Prevent 5th bar from being shown by PanelDialog::show_all_children + gtk_widget_set_no_show_all(_l[4], TRUE); + _s[4]->set_no_show_all(true); + gtk_widget_set_no_show_all(_b[4], TRUE); + + /* Initial mode is none, so it works */ + setMode(mode); +} + +void ColorScales::_recalcColor() +{ + SPColor color; + gfloat alpha = 1.0; + gfloat c[5]; + + switch (_mode) { + case SP_COLOR_SCALES_MODE_RGB: + case SP_COLOR_SCALES_MODE_HSV: + _getRgbaFloatv(c); + color.set(c[0], c[1], c[2]); + alpha = c[3]; + break; + case SP_COLOR_SCALES_MODE_CMYK: { + _getCmykaFloatv(c); + + float rgb[3]; + sp_color_cmyk_to_rgb_floatv(rgb, c[0], c[1], c[2], c[3]); + color.set(rgb[0], rgb[1], rgb[2]); + alpha = c[4]; + break; + } + default: + g_warning("file %s: line %d: Illegal color selector mode %d", __FILE__, __LINE__, _mode); + break; + } + + _color.preserveICC(); + _color.setColorAlpha(color, alpha); +} + +void ColorScales::_updateDisplay() +{ +#ifdef DUMP_CHANGE_INFO + g_message("ColorScales::_onColorChanged( this=%p, %f, %f, %f, %f)", this, _color.color().v.c[0], + _color.color().v.c[1], _color.color().v.c[2], _color.alpha()); +#endif + gfloat tmp[3]; + gfloat c[5] = { 0.0, 0.0, 0.0, 0.0 }; + + SPColor color = _color.color(); + + switch (_mode) { + case SP_COLOR_SCALES_MODE_RGB: + sp_color_get_rgb_floatv(&color, c); + c[3] = _color.alpha(); + c[4] = 0.0; + break; + case SP_COLOR_SCALES_MODE_HSV: + sp_color_get_rgb_floatv(&color, tmp); + sp_color_rgb_to_hsl_floatv(c, tmp[0], tmp[1], tmp[2]); + c[3] = _color.alpha(); + c[4] = 0.0; + break; + case SP_COLOR_SCALES_MODE_CMYK: + sp_color_get_cmyk_floatv(&color, c); + c[4] = _color.alpha(); + break; + default: + g_warning("file %s: line %d: Illegal color selector mode %d", __FILE__, __LINE__, _mode); + break; + } + + _updating = TRUE; + setScaled(_a[0], c[0]); + setScaled(_a[1], c[1]); + setScaled(_a[2], c[2]); + setScaled(_a[3], c[3]); + setScaled(_a[4], c[4]); + _updateSliders(CSC_CHANNELS_ALL); + _updating = FALSE; +} + +/* Helpers for setting color value */ +gfloat ColorScales::getScaled(const GtkAdjustment *a) +{ + gfloat val = gtk_adjustment_get_value(const_cast<GtkAdjustment *>(a)) / + gtk_adjustment_get_upper(const_cast<GtkAdjustment *>(a)); + return val; +} + +void ColorScales::setScaled(GtkAdjustment *a, gfloat v) +{ + gfloat val = v * gtk_adjustment_get_upper(a); + gtk_adjustment_set_value(a, val); +} + +void ColorScales::_setRangeLimit(gdouble upper) +{ + _rangeLimit = upper; + for (gint i = 0; i < static_cast<gint>(G_N_ELEMENTS(_a)); i++) { + gtk_adjustment_set_upper(_a[i], upper); + gtk_adjustment_changed(_a[i]); + } +} + +void ColorScales::_onColorChanged() +{ + if (!get_visible()) { + return; + } + _updateDisplay(); +} + +void ColorScales::on_show() +{ +#if GTK_CHECK_VERSION(3, 0, 0) + Gtk::Grid::on_show(); +#else + Gtk::Table::on_show(); +#endif + _updateDisplay(); +} + +void ColorScales::_getRgbaFloatv(gfloat *rgba) +{ + g_return_if_fail(rgba != NULL); + + switch (_mode) { + case SP_COLOR_SCALES_MODE_RGB: + rgba[0] = getScaled(_a[0]); + rgba[1] = getScaled(_a[1]); + rgba[2] = getScaled(_a[2]); + rgba[3] = getScaled(_a[3]); + break; + case SP_COLOR_SCALES_MODE_HSV: + sp_color_hsl_to_rgb_floatv(rgba, getScaled(_a[0]), getScaled(_a[1]), getScaled(_a[2])); + rgba[3] = getScaled(_a[3]); + break; + case SP_COLOR_SCALES_MODE_CMYK: + sp_color_cmyk_to_rgb_floatv(rgba, getScaled(_a[0]), getScaled(_a[1]), getScaled(_a[2]), getScaled(_a[3])); + rgba[3] = getScaled(_a[4]); + break; + default: + g_warning("file %s: line %d: Illegal color selector mode", __FILE__, __LINE__); + break; + } +} + +void ColorScales::_getCmykaFloatv(gfloat *cmyka) +{ + gfloat rgb[3]; + + g_return_if_fail(cmyka != NULL); + + switch (_mode) { + case SP_COLOR_SCALES_MODE_RGB: + sp_color_rgb_to_cmyk_floatv(cmyka, getScaled(_a[0]), getScaled(_a[1]), getScaled(_a[2])); + cmyka[4] = getScaled(_a[3]); + break; + case SP_COLOR_SCALES_MODE_HSV: + sp_color_hsl_to_rgb_floatv(rgb, getScaled(_a[0]), getScaled(_a[1]), getScaled(_a[2])); + sp_color_rgb_to_cmyk_floatv(cmyka, rgb[0], rgb[1], rgb[2]); + cmyka[4] = getScaled(_a[3]); + break; + case SP_COLOR_SCALES_MODE_CMYK: + cmyka[0] = getScaled(_a[0]); + cmyka[1] = getScaled(_a[1]); + cmyka[2] = getScaled(_a[2]); + cmyka[3] = getScaled(_a[3]); + cmyka[4] = getScaled(_a[4]); + break; + default: + g_warning("file %s: line %d: Illegal color selector mode", __FILE__, __LINE__); + break; + } +} + +guint32 ColorScales::_getRgba32() +{ + gfloat c[4]; + guint32 rgba; + + _getRgbaFloatv(c); + + rgba = SP_RGBA32_F_COMPOSE(c[0], c[1], c[2], c[3]); + + return rgba; +} + +void ColorScales::setMode(SPColorScalesMode mode) +{ + gfloat rgba[4]; + gfloat c[4]; + + if (_mode == mode) + return; + + if ((_mode == SP_COLOR_SCALES_MODE_RGB) || (_mode == SP_COLOR_SCALES_MODE_HSV) || + (_mode == SP_COLOR_SCALES_MODE_CMYK)) { + _getRgbaFloatv(rgba); + } + else { + rgba[0] = rgba[1] = rgba[2] = rgba[3] = 1.0; + } + + _mode = mode; + + switch (mode) { + case SP_COLOR_SCALES_MODE_RGB: + _setRangeLimit(255.0); + gtk_label_set_markup_with_mnemonic(GTK_LABEL(_l[0]), _("_R:")); + _s[0]->set_tooltip_text(_("Red")); + gtk_widget_set_tooltip_text(_b[0], _("Red")); + gtk_label_set_markup_with_mnemonic(GTK_LABEL(_l[1]), _("_G:")); + _s[1]->set_tooltip_text(_("Green")); + gtk_widget_set_tooltip_text(_b[1], _("Green")); + gtk_label_set_markup_with_mnemonic(GTK_LABEL(_l[2]), _("_B:")); + _s[2]->set_tooltip_text(_("Blue")); + gtk_widget_set_tooltip_text(_b[2], _("Blue")); + gtk_label_set_markup_with_mnemonic(GTK_LABEL(_l[3]), _("_A:")); + _s[3]->set_tooltip_text(_("Alpha (opacity)")); + gtk_widget_set_tooltip_text(_b[3], _("Alpha (opacity)")); + _s[0]->setMap(NULL); + gtk_widget_hide(_l[4]); + _s[4]->hide(); + gtk_widget_hide(_b[4]); + _updating = TRUE; + setScaled(_a[0], rgba[0]); + setScaled(_a[1], rgba[1]); + setScaled(_a[2], rgba[2]); + setScaled(_a[3], rgba[3]); + _updateSliders(CSC_CHANNELS_ALL); + _updating = FALSE; + break; + case SP_COLOR_SCALES_MODE_HSV: + _setRangeLimit(255.0); + gtk_label_set_markup_with_mnemonic(GTK_LABEL(_l[0]), _("_H:")); + _s[0]->set_tooltip_text(_("Hue")); + gtk_widget_set_tooltip_text(_b[0], _("Hue")); + gtk_label_set_markup_with_mnemonic(GTK_LABEL(_l[1]), _("_S:")); + _s[1]->set_tooltip_text(_("Saturation")); + gtk_widget_set_tooltip_text(_b[1], _("Saturation")); + gtk_label_set_markup_with_mnemonic(GTK_LABEL(_l[2]), _("_L:")); + _s[2]->set_tooltip_text(_("Lightness")); + gtk_widget_set_tooltip_text(_b[2], _("Lightness")); + gtk_label_set_markup_with_mnemonic(GTK_LABEL(_l[3]), _("_A:")); + _s[3]->set_tooltip_text(_("Alpha (opacity)")); + gtk_widget_set_tooltip_text(_b[3], _("Alpha (opacity)")); + _s[0]->setMap((guchar *)(sp_color_scales_hue_map())); + gtk_widget_hide(_l[4]); + _s[4]->hide(); + gtk_widget_hide(_b[4]); + _updating = TRUE; + c[0] = 0.0; + sp_color_rgb_to_hsl_floatv(c, rgba[0], rgba[1], rgba[2]); + setScaled(_a[0], c[0]); + setScaled(_a[1], c[1]); + setScaled(_a[2], c[2]); + setScaled(_a[3], rgba[3]); + _updateSliders(CSC_CHANNELS_ALL); + _updating = FALSE; + break; + case SP_COLOR_SCALES_MODE_CMYK: + _setRangeLimit(100.0); + gtk_label_set_markup_with_mnemonic(GTK_LABEL(_l[0]), _("_C:")); + _s[0]->set_tooltip_text(_("Cyan")); + gtk_widget_set_tooltip_text(_b[0], _("Cyan")); + gtk_label_set_markup_with_mnemonic(GTK_LABEL(_l[1]), _("_M:")); + _s[1]->set_tooltip_text(_("Magenta")); + gtk_widget_set_tooltip_text(_b[1], _("Magenta")); + gtk_label_set_markup_with_mnemonic(GTK_LABEL(_l[2]), _("_Y:")); + _s[2]->set_tooltip_text(_("Yellow")); + gtk_widget_set_tooltip_text(_b[2], _("Yellow")); + gtk_label_set_markup_with_mnemonic(GTK_LABEL(_l[3]), _("_K:")); + _s[3]->set_tooltip_text(_("Black")); + gtk_widget_set_tooltip_text(_b[3], _("Black")); + gtk_label_set_markup_with_mnemonic(GTK_LABEL(_l[4]), _("_A:")); + _s[4]->set_tooltip_text(_("Alpha (opacity)")); + gtk_widget_set_tooltip_text(_b[4], _("Alpha (opacity)")); + _s[0]->setMap(NULL); + gtk_widget_show(_l[4]); + _s[4]->show(); + gtk_widget_show(_b[4]); + _updating = TRUE; + + sp_color_rgb_to_cmyk_floatv(c, rgba[0], rgba[1], rgba[2]); + setScaled(_a[0], c[0]); + setScaled(_a[1], c[1]); + setScaled(_a[2], c[2]); + setScaled(_a[3], c[3]); + + setScaled(_a[4], rgba[3]); + _updateSliders(CSC_CHANNELS_ALL); + _updating = FALSE; + break; + default: + g_warning("file %s: line %d: Illegal color selector mode", __FILE__, __LINE__); + break; + } +} + +SPColorScalesMode ColorScales::getMode() const { return _mode; } + +void ColorScales::_adjustmentAnyChanged(GtkAdjustment *adjustment, ColorScales *cs) +{ + gint channel = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(adjustment), "channel")); + + _adjustmentChanged(cs, channel); +} + +void ColorScales::_sliderAnyGrabbed() +{ + if (_updating) { + return; + } + if (!_dragging) { + _dragging = TRUE; + _color.setHeld(true); + } +} + +void ColorScales::_sliderAnyReleased() +{ + if (_updating) { + return; + } + if (_dragging) { + _dragging = FALSE; + _color.setHeld(false); + } +} + +void ColorScales::_sliderAnyChanged() +{ + if (_updating) { + return; + } + _recalcColor(); +} + +void ColorScales::_adjustmentChanged(ColorScales *scales, guint channel) +{ + if (scales->_updating) { + return; + } + + scales->_updateSliders((1 << channel)); + scales->_recalcColor(); +} + +void ColorScales::_updateSliders(guint channels) +{ + gfloat rgb0[3], rgbm[3], rgb1[3]; +#ifdef SPCS_PREVIEW + guint32 rgba; +#endif + switch (_mode) { + case SP_COLOR_SCALES_MODE_RGB: + if ((channels != CSC_CHANNEL_R) && (channels != CSC_CHANNEL_A)) { + /* Update red */ + _s[0]->setColors(SP_RGBA32_F_COMPOSE(0.0, getScaled(_a[1]), getScaled(_a[2]), 1.0), + SP_RGBA32_F_COMPOSE(0.5, getScaled(_a[1]), getScaled(_a[2]), 1.0), + SP_RGBA32_F_COMPOSE(1.0, getScaled(_a[1]), getScaled(_a[2]), 1.0)); + } + if ((channels != CSC_CHANNEL_G) && (channels != CSC_CHANNEL_A)) { + /* Update green */ + _s[1]->setColors(SP_RGBA32_F_COMPOSE(getScaled(_a[0]), 0.0, getScaled(_a[2]), 1.0), + SP_RGBA32_F_COMPOSE(getScaled(_a[0]), 0.5, getScaled(_a[2]), 1.0), + SP_RGBA32_F_COMPOSE(getScaled(_a[0]), 1.0, getScaled(_a[2]), 1.0)); + } + if ((channels != CSC_CHANNEL_B) && (channels != CSC_CHANNEL_A)) { + /* Update blue */ + _s[2]->setColors(SP_RGBA32_F_COMPOSE(getScaled(_a[0]), getScaled(_a[1]), 0.0, 1.0), + SP_RGBA32_F_COMPOSE(getScaled(_a[0]), getScaled(_a[1]), 0.5, 1.0), + SP_RGBA32_F_COMPOSE(getScaled(_a[0]), getScaled(_a[1]), 1.0, 1.0)); + } + if (channels != CSC_CHANNEL_A) { + /* Update alpha */ + _s[3]->setColors(SP_RGBA32_F_COMPOSE(getScaled(_a[0]), getScaled(_a[1]), getScaled(_a[2]), 0.0), + SP_RGBA32_F_COMPOSE(getScaled(_a[0]), getScaled(_a[1]), getScaled(_a[2]), 0.5), + SP_RGBA32_F_COMPOSE(getScaled(_a[0]), getScaled(_a[1]), getScaled(_a[2]), 1.0)); + } + break; + case SP_COLOR_SCALES_MODE_HSV: + /* Hue is never updated */ + if ((channels != CSC_CHANNEL_S) && (channels != CSC_CHANNEL_A)) { + /* Update saturation */ + sp_color_hsl_to_rgb_floatv(rgb0, getScaled(_a[0]), 0.0, getScaled(_a[2])); + sp_color_hsl_to_rgb_floatv(rgbm, getScaled(_a[0]), 0.5, getScaled(_a[2])); + sp_color_hsl_to_rgb_floatv(rgb1, getScaled(_a[0]), 1.0, getScaled(_a[2])); + _s[1]->setColors(SP_RGBA32_F_COMPOSE(rgb0[0], rgb0[1], rgb0[2], 1.0), + SP_RGBA32_F_COMPOSE(rgbm[0], rgbm[1], rgbm[2], 1.0), + SP_RGBA32_F_COMPOSE(rgb1[0], rgb1[1], rgb1[2], 1.0)); + } + if ((channels != CSC_CHANNEL_V) && (channels != CSC_CHANNEL_A)) { + /* Update value */ + sp_color_hsl_to_rgb_floatv(rgb0, getScaled(_a[0]), getScaled(_a[1]), 0.0); + sp_color_hsl_to_rgb_floatv(rgbm, getScaled(_a[0]), getScaled(_a[1]), 0.5); + sp_color_hsl_to_rgb_floatv(rgb1, getScaled(_a[0]), getScaled(_a[1]), 1.0); + _s[2]->setColors(SP_RGBA32_F_COMPOSE(rgb0[0], rgb0[1], rgb0[2], 1.0), + SP_RGBA32_F_COMPOSE(rgbm[0], rgbm[1], rgbm[2], 1.0), + SP_RGBA32_F_COMPOSE(rgb1[0], rgb1[1], rgb1[2], 1.0)); + } + if (channels != CSC_CHANNEL_A) { + /* Update alpha */ + sp_color_hsl_to_rgb_floatv(rgb0, getScaled(_a[0]), getScaled(_a[1]), getScaled(_a[2])); + _s[3]->setColors(SP_RGBA32_F_COMPOSE(rgb0[0], rgb0[1], rgb0[2], 0.0), + SP_RGBA32_F_COMPOSE(rgb0[0], rgb0[1], rgb0[2], 0.5), + SP_RGBA32_F_COMPOSE(rgb0[0], rgb0[1], rgb0[2], 1.0)); + } + break; + case SP_COLOR_SCALES_MODE_CMYK: + if ((channels != CSC_CHANNEL_C) && (channels != CSC_CHANNEL_CMYKA)) { + /* Update C */ + sp_color_cmyk_to_rgb_floatv(rgb0, 0.0, getScaled(_a[1]), getScaled(_a[2]), getScaled(_a[3])); + sp_color_cmyk_to_rgb_floatv(rgbm, 0.5, getScaled(_a[1]), getScaled(_a[2]), getScaled(_a[3])); + sp_color_cmyk_to_rgb_floatv(rgb1, 1.0, getScaled(_a[1]), getScaled(_a[2]), getScaled(_a[3])); + _s[0]->setColors(SP_RGBA32_F_COMPOSE(rgb0[0], rgb0[1], rgb0[2], 1.0), + SP_RGBA32_F_COMPOSE(rgbm[0], rgbm[1], rgbm[2], 1.0), + SP_RGBA32_F_COMPOSE(rgb1[0], rgb1[1], rgb1[2], 1.0)); + } + if ((channels != CSC_CHANNEL_M) && (channels != CSC_CHANNEL_CMYKA)) { + /* Update M */ + sp_color_cmyk_to_rgb_floatv(rgb0, getScaled(_a[0]), 0.0, getScaled(_a[2]), getScaled(_a[3])); + sp_color_cmyk_to_rgb_floatv(rgbm, getScaled(_a[0]), 0.5, getScaled(_a[2]), getScaled(_a[3])); + sp_color_cmyk_to_rgb_floatv(rgb1, getScaled(_a[0]), 1.0, getScaled(_a[2]), getScaled(_a[3])); + _s[1]->setColors(SP_RGBA32_F_COMPOSE(rgb0[0], rgb0[1], rgb0[2], 1.0), + SP_RGBA32_F_COMPOSE(rgbm[0], rgbm[1], rgbm[2], 1.0), + SP_RGBA32_F_COMPOSE(rgb1[0], rgb1[1], rgb1[2], 1.0)); + } + if ((channels != CSC_CHANNEL_Y) && (channels != CSC_CHANNEL_CMYKA)) { + /* Update Y */ + sp_color_cmyk_to_rgb_floatv(rgb0, getScaled(_a[0]), getScaled(_a[1]), 0.0, getScaled(_a[3])); + sp_color_cmyk_to_rgb_floatv(rgbm, getScaled(_a[0]), getScaled(_a[1]), 0.5, getScaled(_a[3])); + sp_color_cmyk_to_rgb_floatv(rgb1, getScaled(_a[0]), getScaled(_a[1]), 1.0, getScaled(_a[3])); + _s[2]->setColors(SP_RGBA32_F_COMPOSE(rgb0[0], rgb0[1], rgb0[2], 1.0), + SP_RGBA32_F_COMPOSE(rgbm[0], rgbm[1], rgbm[2], 1.0), + SP_RGBA32_F_COMPOSE(rgb1[0], rgb1[1], rgb1[2], 1.0)); + } + if ((channels != CSC_CHANNEL_K) && (channels != CSC_CHANNEL_CMYKA)) { + /* Update K */ + sp_color_cmyk_to_rgb_floatv(rgb0, getScaled(_a[0]), getScaled(_a[1]), getScaled(_a[2]), 0.0); + sp_color_cmyk_to_rgb_floatv(rgbm, getScaled(_a[0]), getScaled(_a[1]), getScaled(_a[2]), 0.5); + sp_color_cmyk_to_rgb_floatv(rgb1, getScaled(_a[0]), getScaled(_a[1]), getScaled(_a[2]), 1.0); + _s[3]->setColors(SP_RGBA32_F_COMPOSE(rgb0[0], rgb0[1], rgb0[2], 1.0), + SP_RGBA32_F_COMPOSE(rgbm[0], rgbm[1], rgbm[2], 1.0), + SP_RGBA32_F_COMPOSE(rgb1[0], rgb1[1], rgb1[2], 1.0)); + } + if (channels != CSC_CHANNEL_CMYKA) { + /* Update alpha */ + sp_color_cmyk_to_rgb_floatv(rgb0, getScaled(_a[0]), getScaled(_a[1]), getScaled(_a[2]), + getScaled(_a[3])); + _s[4]->setColors(SP_RGBA32_F_COMPOSE(rgb0[0], rgb0[1], rgb0[2], 0.0), + SP_RGBA32_F_COMPOSE(rgb0[0], rgb0[1], rgb0[2], 0.5), + SP_RGBA32_F_COMPOSE(rgb0[0], rgb0[1], rgb0[2], 1.0)); + } + break; + default: + g_warning("file %s: line %d: Illegal color selector mode", __FILE__, __LINE__); + break; + } + +#ifdef SPCS_PREVIEW + rgba = sp_color_scales_get_rgba32(cs); + sp_color_preview_set_rgba32(SP_COLOR_PREVIEW(_p), rgba); +#endif +} + +static const gchar *sp_color_scales_hue_map(void) +{ + static gchar *map = NULL; + + if (!map) { + gchar *p; + gint h; + map = g_new(gchar, 4 * 1024); + p = map; + for (h = 0; h < 1024; h++) { + gfloat rgb[3]; + sp_color_hsl_to_rgb_floatv(rgb, h / 1024.0, 1.0, 0.5); + *p++ = SP_COLOR_F_TO_U(rgb[0]); + *p++ = SP_COLOR_F_TO_U(rgb[1]); + *p++ = SP_COLOR_F_TO_U(rgb[2]); + *p++ = 255; + } + } + + return map; +} + +ColorScalesFactory::ColorScalesFactory(SPColorScalesMode submode) + : _submode(submode) +{ +} + +ColorScalesFactory::~ColorScalesFactory() {} + +Gtk::Widget *ColorScalesFactory::createWidget(Inkscape::UI::SelectedColor &color) const +{ + Gtk::Widget *w = Gtk::manage(new ColorScales(color, _submode)); + return w; +} + +Glib::ustring ColorScalesFactory::modeName() const { + return gettext(ColorScales::SUBMODE_NAMES[_submode]); +} + +} +} +} diff --git a/src/ui/widget/color-scales.h b/src/ui/widget/color-scales.h new file mode 100644 index 000000000..aeacfbcc1 --- /dev/null +++ b/src/ui/widget/color-scales.h @@ -0,0 +1,112 @@ +#ifndef SEEN_SP_COLOR_SCALES_H +#define SEEN_SP_COLOR_SCALES_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#if WITH_GTKMM_3_0 +#include <gtkmm/grid.h> +#else +#include <gtkmm/table.h> +#endif + +#include "ui/selected-color.h" + +namespace Inkscape { +namespace UI { +namespace Widget { + +class ColorSlider; + +typedef enum { + SP_COLOR_SCALES_MODE_NONE = 0, + SP_COLOR_SCALES_MODE_RGB = 1, + SP_COLOR_SCALES_MODE_HSV = 2, + SP_COLOR_SCALES_MODE_CMYK = 3 +} SPColorScalesMode; + +class ColorScales +#if GTK_CHECK_VERSION(3, 0, 0) + : public Gtk::Grid +#else + : public Gtk::Table +#endif +{ +public: + static const gchar *SUBMODE_NAMES[]; + + static gfloat getScaled(const GtkAdjustment *a); + static void setScaled(GtkAdjustment *a, gfloat v); + + ColorScales(SelectedColor &color, SPColorScalesMode mode); + virtual ~ColorScales(); + + virtual void _initUI(SPColorScalesMode mode); + + void setMode(SPColorScalesMode mode); + SPColorScalesMode getMode() const; + +protected: + void _onColorChanged(); + void on_show(); + + static void _adjustmentAnyChanged(GtkAdjustment *adjustment, ColorScales *cs); + void _sliderAnyGrabbed(); + void _sliderAnyReleased(); + void _sliderAnyChanged(); + static void _adjustmentChanged(ColorScales *cs, guint channel); + + void _getRgbaFloatv(gfloat *rgba); + void _getCmykaFloatv(gfloat *cmyka); + guint32 _getRgba32(); + void _updateSliders(guint channels); + void _recalcColor(); + void _updateDisplay(); + + void _setRangeLimit(gdouble upper); + + SelectedColor &_color; + SPColorScalesMode _mode; + gdouble _rangeLimit; + gboolean _updating : 1; + gboolean _dragging : 1; + GtkAdjustment *_a[5]; /* Channel adjustments */ + Inkscape::UI::Widget::ColorSlider *_s[5]; /* Channel sliders */ + GtkWidget *_b[5]; /* Spinbuttons */ + GtkWidget *_l[5]; /* Labels */ + +private: + // By default, disallow copy constructor and assignment operator + ColorScales(ColorScales const &obj); + ColorScales &operator=(ColorScales const &obj); +}; + +class ColorScalesFactory : public Inkscape::UI::ColorSelectorFactory +{ +public: + ColorScalesFactory(SPColorScalesMode submode); + ~ColorScalesFactory(); + + Gtk::Widget *createWidget(Inkscape::UI::SelectedColor &color) const; + Glib::ustring modeName() const; + +private: + SPColorScalesMode _submode; +}; + +} +} +} + +#endif /* !SEEN_SP_COLOR_SCALES_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 : diff --git a/src/ui/widget/color-slider.cpp b/src/ui/widget/color-slider.cpp new file mode 100644 index 000000000..0c9586a67 --- /dev/null +++ b/src/ui/widget/color-slider.cpp @@ -0,0 +1,633 @@ +/** + * @file + * A slider with colored background - implementation. + */ +/* Author: + * Lauris Kaplinski <lauris@kaplinski.com> + * bulia byak <buliabyak@users.sf.net> + * + * Copyright (C) 2001-2002 Lauris Kaplinski + * + * This code is in public domain + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <gdkmm/cursor.h> +#include <gdkmm/screen.h> +#include <gdkmm/general.h> +#include <gtkmm/adjustment.h> +#if WITH_GTKMM_3_0 +#include <gtkmm/stylecontext.h> +#else +#include <gtkmm/style.h> +#endif +#include <gtk/gtk.h> + +#include "ui/widget/color-scales.h" +#include "ui/widget/color-slider.h" +#include "preferences.h" + +static const gint SLIDER_WIDTH = 96; +static const gint SLIDER_HEIGHT = 8; +static const gint ARROW_SIZE = 7; + +static const guchar *sp_color_slider_render_gradient(gint x0, gint y0, gint width, gint height, gint c[], gint dc[], + guint b0, guint b1, guint mask); +static const guchar *sp_color_slider_render_map(gint x0, gint y0, gint width, gint height, guchar *map, gint start, + gint step, guint b0, guint b1, guint mask); + +namespace Inkscape { +namespace UI { +namespace Widget { + +#if GTK_CHECK_VERSION(3, 0, 0) +ColorSlider::ColorSlider(Glib::RefPtr<Gtk::Adjustment> adjustment) + : _dragging(false) +#else +ColorSlider::ColorSlider(Gtk::Adjustment *adjustment) + : _dragging(false) + , _adjustment(NULL) +#endif + , _value(0.0) + , _oldvalue(0.0) + , _mapsize(0) + , _map(NULL) +{ + _c0[0] = 0x00; + _c0[1] = 0x00; + _c0[2] = 0x00; + _c0[3] = 0xff; + + _cm[0] = 0xff; + _cm[1] = 0x00; + _cm[2] = 0x00; + _cm[3] = 0xff; + + _c0[0] = 0xff; + _c0[1] = 0xff; + _c0[2] = 0xff; + _c0[3] = 0xff; + + _b0 = 0x5f; + _b1 = 0xa0; + _bmask = 0x08; + + setAdjustment(adjustment); +} + +ColorSlider::~ColorSlider() +{ + if (_adjustment) { + _adjustment_changed_connection.disconnect(); + _adjustment_value_changed_connection.disconnect(); +#if GTK_CHECK_VERSION(3, 0, 0) + _adjustment.reset(); +#else + _adjustment->unreference(); + _adjustment = NULL; +#endif + } +} + +void ColorSlider::on_realize() +{ + set_realized(); + + if (!_gdk_window) { + GdkWindowAttr attributes; + gint attributes_mask; + Gtk::Allocation allocation = get_allocation(); + + memset(&attributes, 0, sizeof(attributes)); + attributes.x = allocation.get_x(); + attributes.y = allocation.get_y(); + attributes.width = allocation.get_width(); + attributes.height = allocation.get_height(); + attributes.window_type = GDK_WINDOW_CHILD; + attributes.wclass = GDK_INPUT_OUTPUT; + attributes.visual = gdk_screen_get_system_visual(gdk_screen_get_default()); +#if !GTK_CHECK_VERSION(3, 0, 0) + attributes.colormap = gdk_screen_get_system_colormap(gdk_screen_get_default()); +#endif + attributes.event_mask = get_events(); + attributes.event_mask |= (Gdk::EXPOSURE_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK | + Gdk::POINTER_MOTION_MASK | Gdk::ENTER_NOTIFY_MASK | Gdk::LEAVE_NOTIFY_MASK); + +#if GTK_CHECK_VERSION(3, 0, 0) + attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL; +#else + attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP; +#endif + + _gdk_window = Gdk::Window::create(get_parent_window(), &attributes, attributes_mask); + set_window(_gdk_window); + _gdk_window->set_user_data(gobj()); + +#if !GTK_CHECK_VERSION(3, 0, 0) + style_attach(); +#endif + } +} + +void ColorSlider::on_unrealize() +{ + _gdk_window.reset(); + + Gtk::Widget::on_unrealize(); +} + +void ColorSlider::on_size_allocate(Gtk::Allocation &allocation) +{ + set_allocation(allocation); + + if (get_realized()) { + _gdk_window->move_resize(allocation.get_x(), allocation.get_y(), allocation.get_width(), + allocation.get_height()); + } +} + +#if GTK_CHECK_VERSION(3, 0, 0) + +void ColorSlider::get_preferred_width_vfunc(int &minimum_width, int &natural_width) const +{ + Glib::RefPtr<Gtk::StyleContext> style_context = get_style_context(); + Gtk::Border padding = style_context->get_padding(get_state_flags()); + int width = SLIDER_WIDTH + padding.get_left() + padding.get_right(); + minimum_width = natural_width = width; +} + +void ColorSlider::get_preferred_width_for_height_vfunc(int /*height*/, int &minimum_width, int &natural_width) const +{ + get_preferred_width(minimum_width, natural_width); +} + +void ColorSlider::get_preferred_height_vfunc(int &minimum_height, int &natural_height) const +{ + Glib::RefPtr<Gtk::StyleContext> style_context = get_style_context(); + Gtk::Border padding = style_context->get_padding(get_state_flags()); + int height = SLIDER_HEIGHT + padding.get_top() + padding.get_bottom(); + minimum_height = natural_height = height; +} + +void ColorSlider::get_preferred_height_for_width_vfunc(int /*width*/, int &minimum_height, int &natural_height) const +{ + get_preferred_height(minimum_height, natural_height); +} + +#else + +void ColorSlider::on_size_request(Gtk::Requisition *requisition) +{ + GtkStyle *style = gtk_widget_get_style(gobj()); + requisition->width = SLIDER_WIDTH + style->xthickness * 2; + requisition->height = SLIDER_HEIGHT + style->ythickness * 2; +} + +bool ColorSlider::on_expose_event(GdkEventExpose *event) +{ + bool result = false; + + if (get_is_drawable()) { + Cairo::RefPtr<Cairo::Context> cr = _gdk_window->create_cairo_context(); + result = on_draw(cr); + } + return result; +} + +#endif + +bool ColorSlider::on_button_press_event(GdkEventButton *event) +{ + if (event->button == 1) { + Gtk::Allocation allocation = get_allocation(); + gint cx, cw; +#if GTK_CHECK_VERSION(3, 0, 0) + cx = get_style_context()->get_padding(get_state_flags()).get_left(); +#else + cx = get_style()->get_xthickness(); +#endif + cw = allocation.get_width() - 2 * cx; + signal_grabbed.emit(); + _dragging = true; + _oldvalue = _value; + ColorScales::setScaled(_adjustment->gobj(), CLAMP((gfloat)(event->x - cx) / cw, 0.0, 1.0)); + signal_dragged.emit(); + +#if GTK_CHECK_VERSION(3, 0, 0) + gdk_device_grab( + gdk_event_get_device(reinterpret_cast<GdkEvent *>(event)), _gdk_window->gobj(), GDK_OWNERSHIP_NONE, FALSE, + static_cast<GdkEventMask>(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK), NULL, event->time); +#else + gdk_pointer_grab(get_window()->gobj(), FALSE, + static_cast<GdkEventMask>(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK), NULL, NULL, + event->time); +#endif + } + + return false; +} + +bool ColorSlider::on_button_release_event(GdkEventButton *event) +{ + if (event->button == 1) { + +#if GTK_CHECK_VERSION(3, 0, 0) + gdk_device_ungrab(gdk_event_get_device(reinterpret_cast<GdkEvent *>(event)), + gdk_event_get_time(reinterpret_cast<GdkEvent *>(event))); +#else + get_window()->pointer_ungrab(event->time); +#endif + + _dragging = false; + signal_released.emit(); + if (_value != _oldvalue) { + signal_value_changed.emit(); + } + } + + return false; +} + +bool ColorSlider::on_motion_notify_event(GdkEventMotion *event) +{ + if (_dragging) { + gint cx, cw; + Gtk::Allocation allocation = get_allocation(); +#if GTK_CHECK_VERSION(3, 0, 0) + cx = get_style_context()->get_padding(get_state_flags()).get_left(); +#else + cx = get_style()->get_xthickness(); +#endif + cw = allocation.get_width() - 2 * cx; + ColorScales::setScaled(_adjustment->gobj(), CLAMP((gfloat)(event->x - cx) / cw, 0.0, 1.0)); + signal_dragged.emit(); + } + + return false; +} + +#if GTK_CHECK_VERSION(3, 0, 0) +void ColorSlider::setAdjustment(Glib::RefPtr<Gtk::Adjustment> adjustment) +{ +#else +void ColorSlider::setAdjustment(Gtk::Adjustment *adjustment) +{ +#endif + if (!adjustment) { +#if GTK_CHECK_VERSION(3, 0, 0) + _adjustment = Gtk::Adjustment::create(0.0, 0.0, 1.0, 0.01, 0.0, 0.0); +#else + _adjustment = Gtk::manage(new Gtk::Adjustment(0.0, 0.0, 1.0, 0.01, 0.0, 0.0)); +#endif + } + else { + adjustment->set_page_increment(0.0); + adjustment->set_page_size(0.0); + } + + if (_adjustment != adjustment) { + if (_adjustment) { + _adjustment_changed_connection.disconnect(); + _adjustment_value_changed_connection.disconnect(); +#if !GTK_CHECK_VERSION(3, 0, 0) + _adjustment->unreference(); +#endif + } + + _adjustment = adjustment; + _adjustment_changed_connection = + _adjustment->signal_changed().connect(sigc::mem_fun(this, &ColorSlider::_onAdjustmentChanged)); + _adjustment_value_changed_connection = + _adjustment->signal_value_changed().connect(sigc::mem_fun(this, &ColorSlider::_onAdjustmentValueChanged)); + + _value = ColorScales::getScaled(_adjustment->gobj()); + + _onAdjustmentChanged(); + } +} + +void ColorSlider::_onAdjustmentChanged() { queue_draw(); } + +void ColorSlider::_onAdjustmentValueChanged() +{ + if (_value != ColorScales::getScaled(_adjustment->gobj())) { + gint cx, cy, cw, ch; +#if GTK_CHECK_VERSION(3, 0, 0) + Glib::RefPtr<Gtk::StyleContext> style_context = get_style_context(); + Gtk::Allocation allocation = get_allocation(); + Gtk::Border padding = style_context->get_padding(get_state_flags()); + cx = padding.get_left(); + cy = padding.get_top(); +#else + Glib::RefPtr<Gtk::Style> style = get_style(); + Gtk::Allocation allocation = get_allocation(); + cx = style->get_xthickness(); + cy = style->get_ythickness(); +#endif + cw = allocation.get_width() - 2 * cx; + ch = allocation.get_height() - 2 * cy; + if ((gint)(ColorScales::getScaled(_adjustment->gobj()) * cw) != (gint)(_value * cw)) { + gint ax, ay; + gfloat value; + value = _value; + _value = ColorScales::getScaled(_adjustment->gobj()); + ax = (int)(cx + value * cw - ARROW_SIZE / 2 - 2); + ay = cy; + queue_draw_area(ax, ay, ARROW_SIZE + 4, ch); + ax = (int)(cx + _value * cw - ARROW_SIZE / 2 - 2); + ay = cy; + queue_draw_area(ax, ay, ARROW_SIZE + 4, ch); + } + else { + _value = ColorScales::getScaled(_adjustment->gobj()); + } + } +} + +void ColorSlider::setColors(guint32 start, guint32 mid, guint32 end) +{ + // Remove any map, if set + _map = 0; + + _c0[0] = start >> 24; + _c0[1] = (start >> 16) & 0xff; + _c0[2] = (start >> 8) & 0xff; + _c0[3] = start & 0xff; + + _cm[0] = mid >> 24; + _cm[1] = (mid >> 16) & 0xff; + _cm[2] = (mid >> 8) & 0xff; + _cm[3] = mid & 0xff; + + _c1[0] = end >> 24; + _c1[1] = (end >> 16) & 0xff; + _c1[2] = (end >> 8) & 0xff; + _c1[3] = end & 0xff; + + queue_draw(); +} + +void ColorSlider::setMap(const guchar *map) +{ + _map = const_cast<guchar *>(map); + + queue_draw(); +} + +void ColorSlider::setBackground(guint dark, guint light, guint size) +{ + _b0 = dark; + _b1 = light; + _bmask = size; + + queue_draw(); +} + +bool ColorSlider::on_draw(const Cairo::RefPtr<Cairo::Context> &cr) +{ + gboolean colorsOnTop = Inkscape::Preferences::get()->getBool("/options/workarounds/colorsontop", false); + + Gtk::Allocation allocation = get_allocation(); + +#if GTK_CHECK_VERSION(3, 0, 0) + Glib::RefPtr<Gtk::StyleContext> style_context = get_style_context(); +#else + Glib::RefPtr<Gdk::Window> window = get_window(); + Glib::RefPtr<Gtk::Style> style = get_style(); +#endif + + // Draw shadow + if (colorsOnTop) { +#if GTK_CHECK_VERSION(3, 0, 0) + style_context->render_frame(cr, 0, 0, allocation.get_width(), allocation.get_height()); +#else + gtk_paint_shadow(style->gobj(), window->gobj(), gtk_widget_get_state(gobj()), GTK_SHADOW_IN, NULL, gobj(), + "colorslider", 0, 0, allocation.get_width(), allocation.get_height()); +#endif + } + + /* Paintable part of color gradient area */ + Gdk::Rectangle carea; + +#if GTK_CHECK_VERSION(3, 0, 0) + Gtk::Border padding; + + padding = style_context->get_padding(get_state_flags()); + + carea.set_x(padding.get_left()); + carea.set_y(padding.get_top()); + ; +#else + carea.set_x(style->get_xthickness()); + carea.set_y(style->get_ythickness()); +#endif + + carea.set_width(allocation.get_width() - 2 * carea.get_x()); + carea.set_height(allocation.get_height() - 2 * carea.get_y()); + + if (_map) { + /* Render map pixelstore */ + gint d = (1024 << 16) / carea.get_width(); + gint s = 0; + + const guchar *b = + sp_color_slider_render_map(0, 0, carea.get_width(), carea.get_height(), _map, s, d, _b0, _b1, _bmask); + + if (b != NULL && carea.get_width() > 0) { + Glib::RefPtr<Gdk::Pixbuf> pb = Gdk::Pixbuf::create_from_data( + b, Gdk::COLORSPACE_RGB, false, 8, carea.get_width(), carea.get_height(), carea.get_width() * 3); + + Gdk::Cairo::set_source_pixbuf(cr, pb, carea.get_x(), carea.get_y()); + cr->paint(); + } + } + else { + gint c[4], dc[4]; + + /* Render gradient */ + + // part 1: from c0 to cm + if (carea.get_width() > 0) { + for (gint i = 0; i < 4; i++) { + c[i] = _c0[i] << 16; + dc[i] = ((_cm[i] << 16) - c[i]) / (carea.get_width() / 2); + } + guint wi = carea.get_width() / 2; + const guchar *b = sp_color_slider_render_gradient(0, 0, wi, carea.get_height(), c, dc, _b0, _b1, _bmask); + + /* Draw pixelstore 1 */ + if (b != NULL && wi > 0) { + Glib::RefPtr<Gdk::Pixbuf> pb = + Gdk::Pixbuf::create_from_data(b, Gdk::COLORSPACE_RGB, false, 8, wi, carea.get_height(), wi * 3); + + Gdk::Cairo::set_source_pixbuf(cr, pb, carea.get_x(), carea.get_y()); + cr->paint(); + } + } + + // part 2: from cm to c1 + if (carea.get_width() > 0) { + for (gint i = 0; i < 4; i++) { + c[i] = _cm[i] << 16; + dc[i] = ((_c1[i] << 16) - c[i]) / (carea.get_width() / 2); + } + guint wi = carea.get_width() / 2; + const guchar *b = sp_color_slider_render_gradient(carea.get_width() / 2, 0, wi, carea.get_height(), c, dc, + _b0, _b1, _bmask); + + /* Draw pixelstore 2 */ + if (b != NULL && wi > 0) { + Glib::RefPtr<Gdk::Pixbuf> pb = + Gdk::Pixbuf::create_from_data(b, Gdk::COLORSPACE_RGB, false, 8, wi, carea.get_height(), wi * 3); + + Gdk::Cairo::set_source_pixbuf(cr, pb, carea.get_width() / 2 + carea.get_x(), carea.get_y()); + cr->paint(); + } + } + } + + /* Draw shadow */ + if (!colorsOnTop) { +#if GTK_CHECK_VERSION(3, 0, 0) + style_context->render_frame(cr, 0, 0, allocation.get_width(), allocation.get_height()); +#else + gtk_paint_shadow(style->gobj(), window->gobj(), gtk_widget_get_state(gobj()), GTK_SHADOW_IN, NULL, gobj(), + "colorslider", 0, 0, allocation.get_width(), allocation.get_height()); +#endif + } + + /* Draw arrow */ + gint x = (int)(_value * (carea.get_width() - 1) - ARROW_SIZE / 2 + carea.get_x()); + gint y1 = carea.get_y(); + gint y2 = carea.get_y() + carea.get_height() - 1; + cr->set_line_width(1.0); + + // Define top arrow + cr->move_to(x - 0.5, y1 + 0.5); + cr->line_to(x + ARROW_SIZE - 0.5, y1 + 0.5); + cr->line_to(x + (ARROW_SIZE - 1) / 2.0, y1 + ARROW_SIZE / 2.0 + 0.5); + cr->line_to(x - 0.5, y1 + 0.5); + + // Define bottom arrow + cr->move_to(x - 0.5, y2 + 0.5); + cr->line_to(x + ARROW_SIZE - 0.5, y2 + 0.5); + cr->line_to(x + (ARROW_SIZE - 1) / 2.0, y2 - ARROW_SIZE / 2.0 + 0.5); + cr->line_to(x - 0.5, y2 + 0.5); + + // Render both arrows + cr->set_source_rgb(1.0, 1.0, 1.0); + cr->stroke_preserve(); + cr->set_source_rgb(0.0, 0.0, 0.0); + cr->fill(); + + return false; +} + +} // namespace Widget +} // namespace UI +} // namespace Inkscape + +/* Colors are << 16 */ + +static const guchar *sp_color_slider_render_gradient(gint x0, gint y0, gint width, gint height, gint c[], gint dc[], + guint b0, guint b1, guint mask) +{ + static guchar *buf = NULL; + static gint bs = 0; + guchar *dp; + gint x, y; + guint r, g, b, a; + + if (buf && (bs < width * height)) { + g_free(buf); + buf = NULL; + } + if (!buf) { + buf = g_new(guchar, width * height * 3); + bs = width * height; + } + + dp = buf; + r = c[0]; + g = c[1]; + b = c[2]; + a = c[3]; + for (x = x0; x < x0 + width; x++) { + gint cr, cg, cb, ca; + guchar *d; + cr = r >> 16; + cg = g >> 16; + cb = b >> 16; + ca = a >> 16; + d = dp; + for (y = y0; y < y0 + height; y++) { + guint bg, fc; + /* Background value */ + bg = ((x & mask) ^ (y & mask)) ? b0 : b1; + fc = (cr - bg) * ca; + d[0] = bg + ((fc + (fc >> 8) + 0x80) >> 8); + fc = (cg - bg) * ca; + d[1] = bg + ((fc + (fc >> 8) + 0x80) >> 8); + fc = (cb - bg) * ca; + d[2] = bg + ((fc + (fc >> 8) + 0x80) >> 8); + d += 3 * width; + } + r += dc[0]; + g += dc[1]; + b += dc[2]; + a += dc[3]; + dp += 3; + } + + return buf; +} + +/* Positions are << 16 */ + +static const guchar *sp_color_slider_render_map(gint x0, gint y0, gint width, gint height, guchar *map, gint start, + gint step, guint b0, guint b1, guint mask) +{ + static guchar *buf = NULL; + static gint bs = 0; + guchar *dp; + gint x, y; + + if (buf && (bs < width * height)) { + g_free(buf); + buf = NULL; + } + if (!buf) { + buf = g_new(guchar, width * height * 3); + bs = width * height; + } + + dp = buf; + for (x = x0; x < x0 + width; x++) { + gint cr, cg, cb, ca; + guchar *d = dp; + guchar *sp = map + 4 * (start >> 16); + cr = *sp++; + cg = *sp++; + cb = *sp++; + ca = *sp++; + for (y = y0; y < y0 + height; y++) { + guint bg, fc; + /* Background value */ + bg = ((x & mask) ^ (y & mask)) ? b0 : b1; + fc = (cr - bg) * ca; + d[0] = bg + ((fc + (fc >> 8) + 0x80) >> 8); + fc = (cg - bg) * ca; + d[1] = bg + ((fc + (fc >> 8) + 0x80) >> 8); + fc = (cb - bg) * ca; + d[2] = bg + ((fc + (fc >> 8) + 0x80) >> 8); + d += 3 * width; + } + dp += 3; + start += step; + } + + return buf; +} diff --git a/src/ui/widget/color-slider.h b/src/ui/widget/color-slider.h new file mode 100644 index 000000000..253f3123c --- /dev/null +++ b/src/ui/widget/color-slider.h @@ -0,0 +1,110 @@ +/* + * A slider with colored background + * + * Author: + * Lauris Kaplinski <lauris@kaplinski.com> + * + * Copyright (C) 2001-2002 Lauris Kaplinski + * + * This code is in public domain + */ + +#ifndef SEEN_COLOR_SLIDER_H +#define SEEN_COLOR_SLIDER_H + +#include <gtkmm/widget.h> +#include <sigc++/signal.h> + +namespace Inkscape { +namespace UI { +namespace Widget { + +/* + * A slider with colored background + */ +class ColorSlider : public Gtk::Widget { +public: +#if GTK_CHECK_VERSION(3, 0, 0) + ColorSlider(Glib::RefPtr<Gtk::Adjustment> adjustment); +#else + ColorSlider(Gtk::Adjustment *adjustment); +#endif + ~ColorSlider(); + +#if GTK_CHECK_VERSION(3, 0, 0) + void setAdjustment(Glib::RefPtr<Gtk::Adjustment> adjustment); +#else + void setAdjustment(Gtk::Adjustment *adjustment); +#endif + + void setColors(guint32 start, guint32 mid, guint32 end); + + void setMap(const guchar *map); + + void setBackground(guint dark, guint light, guint size); + + sigc::signal<void> signal_grabbed; + sigc::signal<void> signal_dragged; + sigc::signal<void> signal_released; + sigc::signal<void> signal_value_changed; + +protected: + void on_size_allocate(Gtk::Allocation &allocation); + void on_realize(); + void on_unrealize(); + bool on_button_press_event(GdkEventButton *event); + bool on_button_release_event(GdkEventButton *event); + bool on_motion_notify_event(GdkEventMotion *event); + bool on_draw(const Cairo::RefPtr<Cairo::Context> &cr); + +#if GTK_CHECK_VERSION(3, 0, 0) + void get_preferred_width_vfunc(int &minimum_width, int &natural_width) const; + void get_preferred_width_for_height_vfunc(int height, int &minimum_width, int &natural_width) const; + void get_preferred_height_vfunc(int &minimum_height, int &natural_height) const; + void get_preferred_height_for_width_vfunc(int width, int &minimum_height, int &natural_height) const; +#else + void on_size_request(Gtk::Requisition *requisition); + bool on_expose_event(GdkEventExpose *event); +#endif + +private: + void _onAdjustmentChanged(); + void _onAdjustmentValueChanged(); + + bool _dragging; + +#if GTK_CHECK_VERSION(3, 0, 0) + Glib::RefPtr<Gtk::Adjustment> _adjustment; +#else + Gtk::Adjustment *_adjustment; +#endif + sigc::connection _adjustment_changed_connection; + sigc::connection _adjustment_value_changed_connection; + + gfloat _value; + gfloat _oldvalue; + guchar _c0[4], _cm[4], _c1[4]; + guchar _b0, _b1; + guchar _bmask; + + gint _mapsize; + guchar *_map; + + Glib::RefPtr<Gdk::Window> _gdk_window; +}; + +} // namespace Widget +} // namespace UI +} // namespace Inkscape + +#endif +/* + 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/ui/widget/color-wheel-selector.cpp b/src/ui/widget/color-wheel-selector.cpp new file mode 100644 index 000000000..ed3400bb5 --- /dev/null +++ b/src/ui/widget/color-wheel-selector.cpp @@ -0,0 +1,302 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "color-wheel-selector.h" + +#include <math.h> +#include <gtk/gtk.h> +#include <glibmm/i18n.h> +#include <gtkmm/adjustment.h> +#include <gtkmm/label.h> +#include <gtkmm/spinbutton.h> +#include "svg/svg-icc-color.h" +#include "ui/dialog-events.h" +#include "ui/selected-color.h" +#include "ui/widget/color-scales.h" +#include "ui/widget/color-slider.h" +#include "ui/widget/gimpcolorwheel.h" + +namespace Inkscape { +namespace UI { +namespace Widget { + + +#define XPAD 4 +#define YPAD 1 + + +const gchar *ColorWheelSelector::MODE_NAME = N_("Wheel"); + +ColorWheelSelector::ColorWheelSelector(SelectedColor &color) +#if GTK_CHECK_VERSION(3, 0, 0) + : Gtk::Grid() +#else + : Gtk::Table(5, 3, false) +#endif + , _color(color) + , _updating(false) +#if !GTK_CHECK_VERSION(3, 0, 0) + , _alpha_adjustment(NULL) +#endif + , _wheel(0) + , _slider(0) +{ + _initUI(); + _color_changed_connection = color.signal_changed.connect(sigc::mem_fun(this, &ColorWheelSelector::_colorChanged)); + _color_dragged_connection = color.signal_dragged.connect(sigc::mem_fun(this, &ColorWheelSelector::_colorChanged)); +} + +ColorWheelSelector::~ColorWheelSelector() +{ + _wheel = 0; +#if !GTK_CHECK_VERSION(3, 0, 0) + delete _alpha_adjustment; +#endif + + _color_changed_connection.disconnect(); + _color_dragged_connection.disconnect(); +} + +void ColorWheelSelector::_initUI() +{ + /* Create components */ + gint row = 0; + + _wheel = gimp_color_wheel_new(); + gtk_widget_show(_wheel); + +#if GTK_CHECK_VERSION(3, 0, 0) + gtk_widget_set_halign(_wheel, GTK_ALIGN_FILL); + gtk_widget_set_valign(_wheel, GTK_ALIGN_FILL); + gtk_widget_set_hexpand(_wheel, TRUE); + gtk_widget_set_vexpand(_wheel, TRUE); + gtk_grid_attach(GTK_GRID(gobj()), _wheel, 0, row, 3, 1); +#else + gtk_table_attach(GTK_TABLE(gobj()), _wheel, 0, 3, row, row + 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), + (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0); +#endif + + row++; + + /* Label */ + Gtk::Label *label = Gtk::manage(new Gtk::Label(_("_A:"), true)); + label->set_alignment(1.0, 0.5); + label->show(); + +#if GTK_CHECK_VERSION(3, 0, 0) + #if GTK_CHECK_VERSION(3, 12, 0) + label->set_margin_start(XPAD); + label->set_margin_end(XPAD); + #else + label->set_margin_left(XPAD); + label->set_margin_right(XPAD); + #endif + label->set_margin_top(YPAD); + label->set_margin_bottom(YPAD); + label->set_halign(Gtk::ALIGN_FILL); + label->set_valign(Gtk::ALIGN_FILL); + attach(*label, 0, row, 1, 1); +#else + attach(*label, 0, 1, row, row + 1, Gtk::FILL, Gtk::FILL, XPAD, YPAD); +#endif + +/* Adjustment */ +#if GTK_CHECK_VERSION(3, 0, 0) + _alpha_adjustment = Gtk::Adjustment::create(0.0, 0.0, 255.0, 1.0, 10.0, 10.0); +#else + _alpha_adjustment = new Gtk::Adjustment(0.0, 0.0, 255.0, 1.0, 10.0, 10.0); +#endif + /* Slider */ + _slider = Gtk::manage(new Inkscape::UI::Widget::ColorSlider(_alpha_adjustment)); + _slider->set_tooltip_text(_("Alpha (opacity)")); + _slider->show(); + +#if GTK_CHECK_VERSION(3, 0, 0) + #if GTK_CHECK_VERSION(3, 12, 0) + _slider->set_margin_start(XPAD); + _slider->set_margin_end(XPAD); + #else + _slider->set_margin_left(XPAD); + _slider->set_margin_right(XPAD); + #endif + _slider->set_margin_top(YPAD); + _slider->set_margin_bottom(YPAD); + _slider->set_hexpand(true); + _slider->set_halign(Gtk::ALIGN_FILL); + _slider->set_valign(Gtk::ALIGN_FILL); + attach(*_slider, 1, row, 1, 1); +#else + attach(*_slider, 1, 2, row, row + 1, Gtk::EXPAND | Gtk::FILL, Gtk::FILL, XPAD, YPAD); +#endif + + _slider->setColors(SP_RGBA32_F_COMPOSE(1.0, 1.0, 1.0, 0.0), SP_RGBA32_F_COMPOSE(1.0, 1.0, 1.0, 0.5), + SP_RGBA32_F_COMPOSE(1.0, 1.0, 1.0, 1.0)); + +/* Spinbutton */ +#if GTK_CHECK_VERSION(3, 0, 0) + Gtk::SpinButton *spin_button = Gtk::manage(new Gtk::SpinButton(_alpha_adjustment, 1.0, 0)); +#else + Gtk::SpinButton *spin_button = Gtk::manage(new Gtk::SpinButton(*_alpha_adjustment, 1.0, 0)); +#endif + spin_button->set_tooltip_text(_("Alpha (opacity)")); + sp_dialog_defocus_on_enter(GTK_WIDGET(spin_button->gobj())); + label->set_mnemonic_widget(*spin_button); + spin_button->show(); + +#if GTK_CHECK_VERSION(3, 0, 0) + #if GTK_CHECK_VERSION(3, 12, 0) + spin_button->set_margin_start(XPAD); + spin_button->set_margin_end(XPAD); + #else + spin_button->set_margin_left(XPAD); + spin_button->set_margin_right(XPAD); + #endif + spin_button->set_margin_top(YPAD); + spin_button->set_margin_bottom(YPAD); + spin_button->set_halign(Gtk::ALIGN_CENTER); + spin_button->set_valign(Gtk::ALIGN_CENTER); + attach(*spin_button, 2, row, 1, 1); +#else + attach(*spin_button, 2, 3, row, row + 1, (Gtk::AttachOptions)0, (Gtk::AttachOptions)0, XPAD, YPAD); +#endif + + /* Signals */ + _alpha_adjustment->signal_value_changed().connect(sigc::mem_fun(this, &ColorWheelSelector::_adjustmentChanged)); + _slider->signal_grabbed.connect(sigc::mem_fun(*this, &ColorWheelSelector::_sliderGrabbed)); + _slider->signal_released.connect(sigc::mem_fun(*this, &ColorWheelSelector::_sliderReleased)); + _slider->signal_value_changed.connect(sigc::mem_fun(*this, &ColorWheelSelector::_sliderChanged)); + + g_signal_connect(G_OBJECT(_wheel), "changed", G_CALLBACK(_wheelChanged), this); +} + +void ColorWheelSelector::on_show() +{ +#if GTK_CHECK_VERSION(3, 0, 0) + Gtk::Grid::on_show(); +#else + Gtk::Table::on_show(); +#endif + _updateDisplay(); +} + +void ColorWheelSelector::_colorChanged() +{ + _updateDisplay(); +} + +void ColorWheelSelector::_adjustmentChanged() +{ + if (_updating) { + return; + } + + // TODO check this. It looks questionable: + // if a value is entered between 0 and 1 exclusive, normalize it to (int) 0..255 or 0..100 + gdouble value = _alpha_adjustment->get_value(); + gdouble upper = _alpha_adjustment->get_upper(); + if (value > 0.0 && value < 1.0) { + _alpha_adjustment->set_value(floor(value * upper + 0.5)); + } + + _color.preserveICC(); + _color.setAlpha(ColorScales::getScaled(_alpha_adjustment->gobj())); +} + +void ColorWheelSelector::_sliderGrabbed() +{ + _color.preserveICC(); + _color.setHeld(true); +} + +void ColorWheelSelector::_sliderReleased() +{ + _color.preserveICC(); + _color.setHeld(false); +} + +void ColorWheelSelector::_sliderChanged() +{ + if (_updating) { + return; + } + + _color.preserveICC(); + _color.setAlpha(ColorScales::getScaled(_alpha_adjustment->gobj())); +} + +void ColorWheelSelector::_wheelChanged(GimpColorWheel *wheel, ColorWheelSelector *wheelSelector) +{ + if (wheelSelector->_updating) { + return; + } + + gdouble h = 0; + gdouble s = 0; + gdouble v = 0; + gimp_color_wheel_get_color(wheel, &h, &s, &v); + + float rgb[3] = { 0, 0, 0 }; + sp_color_hsv_to_rgb_floatv(rgb, h, s, v); + + SPColor color(rgb[0], rgb[1], rgb[2]); + + guint32 start = color.toRGBA32(0x00); + guint32 mid = color.toRGBA32(0x7f); + guint32 end = color.toRGBA32(0xff); + + wheelSelector->_slider->setColors(start, mid, end); + + wheelSelector->_color.preserveICC(); + + wheelSelector->_color.setHeld(gimp_color_wheel_is_adjusting(wheel)); + wheelSelector->_color.setColor(color); +} + +void ColorWheelSelector::_updateDisplay() +{ +#ifdef DUMP_CHANGE_INFO + g_message("ColorWheelSelector::_colorChanged( this=%p, %f, %f, %f, %f)", this, _color.color().v.c[0], + _color.color().v.c[1], _color.color().v.c[2], alpha); +#endif + + bool oldval = _updating; + _updating = true; + { + float hsv[3] = { 0, 0, 0 }; + sp_color_rgb_to_hsv_floatv(hsv, _color.color().v.c[0], _color.color().v.c[1], _color.color().v.c[2]); + gimp_color_wheel_set_color(GIMP_COLOR_WHEEL(_wheel), hsv[0], hsv[1], hsv[2]); + } + + guint32 start = _color.color().toRGBA32(0x00); + guint32 mid = _color.color().toRGBA32(0x7f); + guint32 end = _color.color().toRGBA32(0xff); + + _slider->setColors(start, mid, end); + + ColorScales::setScaled(_alpha_adjustment->gobj(), _color.alpha()); + + _updating = oldval; +} + + +Gtk::Widget *ColorWheelSelectorFactory::createWidget(Inkscape::UI::SelectedColor &color) const +{ + Gtk::Widget *w = Gtk::manage(new ColorWheelSelector(color)); + return w; +} + +Glib::ustring ColorWheelSelectorFactory::modeName() const { return gettext(ColorWheelSelector::MODE_NAME); } +} +} +} +/* + 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/ui/widget/color-wheel-selector.h b/src/ui/widget/color-wheel-selector.h new file mode 100644 index 000000000..5711d417c --- /dev/null +++ b/src/ui/widget/color-wheel-selector.h @@ -0,0 +1,101 @@ +/** + * @file + * Color selector widget containing GIMP color wheel and slider + */ +/* Authors: + * Tomasz Boczkowski <penginsbacon@gmail.com> (c++-sification) + * + * Copyright (C) 2014 Authors + * + * This code is in public domain + */ +#ifndef SEEN_SP_COLOR_WHEEL_SELECTOR_H +#define SEEN_SP_COLOR_WHEEL_SELECTOR_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#if WITH_GTKMM_3_0 +#include <gtkmm/grid.h> +#else +#include <gtkmm/table.h> +#endif + +#include "ui/selected-color.h" + +typedef struct _GimpColorWheel GimpColorWheel; + +namespace Inkscape { +namespace UI { +namespace Widget { + +class ColorSlider; + +class ColorWheelSelector +#if GTK_CHECK_VERSION(3, 0, 0) + : public Gtk::Grid +#else + : public Gtk::Table +#endif +{ +public: + static const gchar *MODE_NAME; + + ColorWheelSelector(SelectedColor &color); + virtual ~ColorWheelSelector(); + +protected: + void _initUI(); + + void on_show(); + + void _colorChanged(); + void _adjustmentChanged(); + void _sliderGrabbed(); + void _sliderReleased(); + void _sliderChanged(); + static void _wheelChanged(GimpColorWheel *wheel, ColorWheelSelector *cs); + + void _updateDisplay(); + + SelectedColor &_color; + bool _updating; +#if GTK_CHECK_VERSION(3, 0, 0) + Glib::RefPtr<Gtk::Adjustment> _alpha_adjustment; +#else + Gtk::Adjustment *_alpha_adjustment; +#endif + GtkWidget *_wheel; + Inkscape::UI::Widget::ColorSlider *_slider; + +private: + // By default, disallow copy constructor and assignment operator + ColorWheelSelector(const ColorWheelSelector &obj); + ColorWheelSelector &operator=(const ColorWheelSelector &obj); + + sigc::connection _color_changed_connection; + sigc::connection _color_dragged_connection; +}; + +class ColorWheelSelectorFactory : public ColorSelectorFactory { +public: + Gtk::Widget *createWidget(SelectedColor &color) const; + Glib::ustring modeName() const; +}; +} +} +} + +#endif // SEEN_SP_COLOR_WHEEL_SELECTOR_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/ui/widget/dock-item.h b/src/ui/widget/dock-item.h index cc0c13eea..25a69d94c 100644 --- a/src/ui/widget/dock-item.h +++ b/src/ui/widget/dock-item.h @@ -15,10 +15,6 @@ # include <config.h> #endif -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include <gtkmm/box.h> #include <gtkmm/frame.h> #include <gtkmm/window.h> diff --git a/src/ui/widget/dock.h b/src/ui/widget/dock.h index 33e60b836..74b072d22 100644 --- a/src/ui/widget/dock.h +++ b/src/ui/widget/dock.h @@ -12,14 +12,6 @@ #ifndef INKSCAPE_UI_WIDGET_DOCK_H #define INKSCAPE_UI_WIDGET_DOCK_H -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif - -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include <gtkmm/box.h> #include <list> #include "ui/widget/dock-item.h" diff --git a/src/ui/widget/entity-entry.cpp b/src/ui/widget/entity-entry.cpp index c7d5efe29..a8de2f384 100644 --- a/src/ui/widget/entity-entry.cpp +++ b/src/ui/widget/entity-entry.cpp @@ -17,10 +17,6 @@ # include <config.h> #endif -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include <gtkmm/scrolledwindow.h> #include <gtkmm/entry.h> @@ -192,8 +188,7 @@ EntityMultiLineEntry::on_changed() Gtk::TextView *tv = static_cast<Gtk::TextView*>(s->get_child()); Glib::ustring text = tv->get_buffer()->get_text(); if (rdf_set_work_entity (doc, _entity, text.c_str())) { - DocumentUndo::done(doc, SP_VERB_NONE, - /* TODO: annotate */ "entity-entry.cpp:146"); + DocumentUndo::done(doc, SP_VERB_NONE, "Document metadata updated"); } _wr->setUpdating (false); } diff --git a/src/ui/widget/filter-effect-chooser.h b/src/ui/widget/filter-effect-chooser.h index 6092c61a5..0bcf97433 100644 --- a/src/ui/widget/filter-effect-chooser.h +++ b/src/ui/widget/filter-effect-chooser.h @@ -12,14 +12,6 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif - -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include <gtkmm/box.h> #include <gtkmm/combobox.h> diff --git a/src/ui/widget/font-variants.cpp b/src/ui/widget/font-variants.cpp new file mode 100644 index 000000000..5d1e40971 --- /dev/null +++ b/src/ui/widget/font-variants.cpp @@ -0,0 +1,719 @@ +/* + * Author: + * Tavmjong Bah <tavmjong@free.fr> + * + * Copyright (C) 2015 Tavmong Bah + * + * Released under GNU GPL. Read the file 'COPYING' for more information. + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <gtkmm.h> +#include <glibmm/i18n.h> +#include <libnrtype/font-instance.h> +#include <iostream> + +#include "font-variants.h" + +// For updating from selection +#include "desktop.h" +#include "selection.h" +#include "style.h" +#include "sp-text.h" +#include "sp-tspan.h" +#include "sp-tref.h" +#include "sp-textpath.h" +#include "sp-item-group.h" +#include "xml/repr.h" + +namespace Inkscape { +namespace UI { +namespace Widget { + + FontVariants::FontVariants () : + Gtk::VBox (), + _ligatures_frame ( Glib::ustring(_("Ligatures" )) ), + _ligatures_common ( Glib::ustring(_("Common" )) ), + _ligatures_discretionary ( Glib::ustring(_("Discretionary")) ), + _ligatures_historical ( Glib::ustring(_("Historical" )) ), + _ligatures_contextual ( Glib::ustring(_("Contextual" )) ), + + _position_frame ( Glib::ustring(_("Position" )) ), + _position_normal ( Glib::ustring(_("Normal" )) ), + _position_sub ( Glib::ustring(_("Subscript" )) ), + _position_super ( Glib::ustring(_("Superscript" )) ), + + _caps_frame ( Glib::ustring(_("Capitals" )) ), + _caps_normal ( Glib::ustring(_("Normal" )) ), + _caps_small ( Glib::ustring(_("Small" )) ), + _caps_all_small ( Glib::ustring(_("All small" )) ), + _caps_petite ( Glib::ustring(_("Petite" )) ), + _caps_all_petite ( Glib::ustring(_("All petite" )) ), + _caps_unicase ( Glib::ustring(_("Unicase" )) ), + _caps_titling ( Glib::ustring(_("Titling" )) ), + + _numeric_frame ( Glib::ustring(_("Numeric" )) ), + _numeric_lining ( Glib::ustring(_("Lining" )) ), + _numeric_old_style ( Glib::ustring(_("Old Style" )) ), + _numeric_default_style ( Glib::ustring(_("Default Style")) ), + _numeric_proportional ( Glib::ustring(_("Proportional" )) ), + _numeric_tabular ( Glib::ustring(_("Tabular" )) ), + _numeric_default_width ( Glib::ustring(_("Default Width")) ), + _numeric_diagonal ( Glib::ustring(_("Diagonal" )) ), + _numeric_stacked ( Glib::ustring(_("Stacked" )) ), + _numeric_default_fractions( Glib::ustring(_("Default Fractions")) ), + _numeric_ordinal ( Glib::ustring(_("Ordinal" )) ), + _numeric_slashed_zero ( Glib::ustring(_("Slashed Zero" )) ), + + _feature_frame ( Glib::ustring(_("Feature Settings")) ), + _feature_label ( Glib::ustring(_("Selection has different Feature Settings!")) ), + + _ligatures_changed( false ), + _position_changed( false ), + _caps_changed( false ), + _numeric_changed( false ) + + { + + // Ligatures -------------------------- + + // Add tooltips + _ligatures_common.set_tooltip_text( + _("Common ligatures. On by default. OpenType tables: 'liga', 'clig'")); + _ligatures_discretionary.set_tooltip_text( + _("Discretionary ligatures. Off by default. OpenType table: 'dlig'")); + _ligatures_historical.set_tooltip_text( + _("Historical ligatures. Off by default. OpenType table: 'hlig'")); + _ligatures_contextual.set_tooltip_text( + _("Contextual forms. On by default. OpenType table: 'calt'")); + + // Add signals + _ligatures_common.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::ligatures_callback) ); + _ligatures_discretionary.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::ligatures_callback) ); + _ligatures_historical.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::ligatures_callback) ); + _ligatures_contextual.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::ligatures_callback) ); + + // Add to frame + _ligatures_vbox.add( _ligatures_common ); + _ligatures_vbox.add( _ligatures_discretionary ); + _ligatures_vbox.add( _ligatures_historical ); + _ligatures_vbox.add( _ligatures_contextual ); + _ligatures_frame.add( _ligatures_vbox ); + add( _ligatures_frame ); + + ligatures_init(); + + // Position ---------------------------------- + + // Add tooltips + _position_normal.set_tooltip_text( _("Normal position.")); + _position_sub.set_tooltip_text( _("Subscript. OpenType table: 'subs'") ); + _position_super.set_tooltip_text( _("Superscript. OpenType table: 'sups'") ); + + // Group buttons + Gtk::RadioButton::Group position_group = _position_normal.get_group(); + _position_sub.set_group(position_group); + _position_super.set_group(position_group); + + // Add signals + _position_normal.signal_pressed().connect ( sigc::mem_fun(*this, &FontVariants::position_callback) ); + _position_sub.signal_pressed().connect ( sigc::mem_fun(*this, &FontVariants::position_callback) ); + _position_super.signal_pressed().connect ( sigc::mem_fun(*this, &FontVariants::position_callback) ); + + // Add to frame + _position_vbox.add( _position_normal ); + _position_vbox.add( _position_sub ); + _position_vbox.add( _position_super ); + _position_frame.add( _position_vbox ); + add( _position_frame ); + + position_init(); + + // Caps ---------------------------------- + + // Add tooltips + _caps_normal.set_tooltip_text( _("Normal capitalization.")); + _caps_small.set_tooltip_text( _("Small-caps (lowercase). OpenType table: 'smcp'")); + _caps_all_small.set_tooltip_text( _("All small-caps (uppercase and lowercase). OpenType tables: 'c2sc' and 'smcp'")); + _caps_petite.set_tooltip_text( _("Petite-caps (lowercase). OpenType table: 'pcap'")); + _caps_all_petite.set_tooltip_text( _("All petite-caps (uppercase and lowercase). OpenType tables: 'c2sc' and 'pcap'")); + _caps_unicase.set_tooltip_text( _("Unicase (small caps for uppercase, normal for lowercase). OpenType table: 'unic'")); + _caps_titling.set_tooltip_text( _("Titling caps (lighter-weight uppercase for use in titles). OpenType table: 'titl'")); + + // Group buttons + Gtk::RadioButton::Group caps_group = _caps_normal.get_group(); + _caps_small.set_group(caps_group); + _caps_all_small.set_group(caps_group); + _caps_petite.set_group(caps_group); + _caps_all_petite.set_group(caps_group); + _caps_unicase.set_group(caps_group); + _caps_titling.set_group(caps_group); + + // Add signals + _caps_normal.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::caps_callback) ); + _caps_small.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::caps_callback) ); + _caps_all_small.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::caps_callback) ); + _caps_petite.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::caps_callback) ); + _caps_all_petite.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::caps_callback) ); + _caps_unicase.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::caps_callback) ); + _caps_titling.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::caps_callback) ); + + // Add to frame + _caps_vbox.add( _caps_normal ); + _caps_vbox.add( _caps_small ); + _caps_vbox.add( _caps_all_small ); + _caps_vbox.add( _caps_petite ); + _caps_vbox.add( _caps_all_petite ); + _caps_vbox.add( _caps_unicase ); + _caps_vbox.add( _caps_titling ); + _caps_frame.add( _caps_vbox ); + add( _caps_frame ); + + caps_init(); + + // Numeric ------------------------------ + + // Add tooltips + _numeric_default_style.set_tooltip_text( _("Normal style.")); + _numeric_lining.set_tooltip_text( _("Lining numerals. OpenType table: 'lnum'")); + _numeric_old_style.set_tooltip_text( _("Old style numerals. OpenType table: 'onum'")); + _numeric_default_width.set_tooltip_text( _("Normal widths.")); + _numeric_proportional.set_tooltip_text( _("Proportional width numerals. OpenType table: 'pnum'")); + _numeric_tabular.set_tooltip_text( _("Same width numerals. OpenType table: 'tnum'")); + _numeric_default_fractions.set_tooltip_text( _("Normal fractions.")); + _numeric_diagonal.set_tooltip_text( _("Diagonal fractions. OpenType table: 'frac'")); + _numeric_stacked.set_tooltip_text( _("Stacked fractions. OpenType table: 'afrc'")); + _numeric_ordinal.set_tooltip_text( _("Ordinals (raised 'th', etc.). OpenType table: 'ordn'")); + _numeric_slashed_zero.set_tooltip_text( _("Slashed zeros. OpenType table: 'zero'")); + + // Group buttons + Gtk::RadioButton::Group style_group = _numeric_default_style.get_group(); + _numeric_lining.set_group(style_group); + _numeric_old_style.set_group(style_group); + + Gtk::RadioButton::Group width_group = _numeric_default_width.get_group(); + _numeric_proportional.set_group(width_group); + _numeric_tabular.set_group(width_group); + + Gtk::RadioButton::Group fraction_group = _numeric_default_fractions.get_group(); + _numeric_diagonal.set_group(fraction_group); + _numeric_stacked.set_group(fraction_group); + + // Add signals + _numeric_default_style.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::numeric_callback) ); + _numeric_lining.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::numeric_callback) ); + _numeric_old_style.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::numeric_callback) ); + _numeric_default_width.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::numeric_callback) ); + _numeric_proportional.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::numeric_callback) ); + _numeric_tabular.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::numeric_callback) ); + _numeric_default_fractions.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::numeric_callback) ); + _numeric_diagonal.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::numeric_callback) ); + _numeric_stacked.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::numeric_callback) ); + _numeric_ordinal.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::numeric_callback) ); + _numeric_slashed_zero.signal_clicked().connect ( sigc::mem_fun(*this, &FontVariants::numeric_callback) ); + + // Add to frame + _numeric_stylebox.add( _numeric_default_style ); + _numeric_stylebox.add( _numeric_lining ); + _numeric_stylebox.add( _numeric_old_style ); + _numeric_vbox.add( _numeric_stylebox ); + _numeric_widthbox.add( _numeric_default_width ); + _numeric_widthbox.add( _numeric_proportional ); + _numeric_widthbox.add( _numeric_tabular ); + _numeric_vbox.add( _numeric_widthbox ); + _numeric_fractionbox.add( _numeric_default_fractions ); + _numeric_fractionbox.add( _numeric_diagonal ); + _numeric_fractionbox.add( _numeric_stacked ); + _numeric_vbox.add( _numeric_fractionbox ); + _numeric_vbox.add( _numeric_ordinal ); + _numeric_vbox.add( _numeric_slashed_zero ); + _numeric_frame.add( _numeric_vbox ); + add( _numeric_frame ); + + + // Feature settings --------------------- + + // Add tooltips + _feature_entry.set_tooltip_text( _("Feature settings in CSS form. No sanity checking is performed.")); + + // Add to frame + _feature_vbox.add( _feature_entry ); + _feature_vbox.add( _feature_label ); + _feature_frame.add( _feature_vbox ); + add( _feature_frame ); + + // Add signals + //_feature_entry.signal_key_press_event().connect ( sigc::mem_fun(*this, &FontVariants::feature_callback) ); + _feature_entry.signal_changed().connect( sigc::mem_fun(*this, &FontVariants::feature_callback) ); + + show_all_children(); + + } + + void + FontVariants::ligatures_init() { + // std::cout << "FontVariants::ligatures_init()" << std::endl; + } + + void + FontVariants::ligatures_callback() { + // std::cout << "FontVariants::ligatures_callback()" << std::endl; + _ligatures_changed = true; + _changed_signal.emit(); + } + + void + FontVariants::position_init() { + // std::cout << "FontVariants::position_init()" << std::endl; + } + + void + FontVariants::position_callback() { + // std::cout << "FontVariants::position_callback()" << std::endl; + _position_changed = true; + _changed_signal.emit(); + } + + void + FontVariants::caps_init() { + // std::cout << "FontVariants::caps_init()" << std::endl; + } + + void + FontVariants::caps_callback() { + // std::cout << "FontVariants::caps_callback()" << std::endl; + _caps_changed = true; + _changed_signal.emit(); + } + + void + FontVariants::numeric_init() { + // std::cout << "FontVariants::numeric_init()" << std::endl; + } + + void + FontVariants::numeric_callback() { + // std::cout << "FontVariants::numeric_callback()" << std::endl; + _numeric_changed = true; + _changed_signal.emit(); + } + + void + FontVariants::feature_init() { + // std::cout << "FontVariants::feature_init()" << std::endl; + } + + void + FontVariants::feature_callback() { + // std::cout << "FontVariants::feature_callback()" << std::endl; + _feature_changed = true; + _changed_signal.emit(); + } + + // Update GUI based on query. + void + FontVariants::update( SPStyle const *query, bool different_features, Glib::ustring& font_spec ) { + + _ligatures_all = query->font_variant_ligatures.computed; + _ligatures_mix = query->font_variant_ligatures.value; + + _ligatures_common.set_active( _ligatures_all & SP_CSS_FONT_VARIANT_LIGATURES_COMMON ); + _ligatures_discretionary.set_active(_ligatures_all & SP_CSS_FONT_VARIANT_LIGATURES_DISCRETIONARY ); + _ligatures_historical.set_active( _ligatures_all & SP_CSS_FONT_VARIANT_LIGATURES_HISTORICAL ); + _ligatures_contextual.set_active( _ligatures_all & SP_CSS_FONT_VARIANT_LIGATURES_CONTEXTUAL ); + + _ligatures_common.set_inconsistent( _ligatures_mix & SP_CSS_FONT_VARIANT_LIGATURES_COMMON ); + _ligatures_discretionary.set_inconsistent( _ligatures_mix & SP_CSS_FONT_VARIANT_LIGATURES_DISCRETIONARY ); + _ligatures_historical.set_inconsistent( _ligatures_mix & SP_CSS_FONT_VARIANT_LIGATURES_HISTORICAL ); + _ligatures_contextual.set_inconsistent( _ligatures_mix & SP_CSS_FONT_VARIANT_LIGATURES_CONTEXTUAL ); + + _position_all = query->font_variant_position.computed; + _position_mix = query->font_variant_position.value; + + _position_normal.set_active( _position_all & SP_CSS_FONT_VARIANT_POSITION_NORMAL ); + _position_sub.set_active( _position_all & SP_CSS_FONT_VARIANT_POSITION_SUB ); + _position_super.set_active( _position_all & SP_CSS_FONT_VARIANT_POSITION_SUPER ); + + _position_normal.set_inconsistent( _position_mix & SP_CSS_FONT_VARIANT_POSITION_NORMAL ); + _position_sub.set_inconsistent( _position_mix & SP_CSS_FONT_VARIANT_POSITION_SUB ); + _position_super.set_inconsistent( _position_mix & SP_CSS_FONT_VARIANT_POSITION_SUPER ); + + _caps_all = query->font_variant_caps.computed; + _caps_mix = query->font_variant_caps.value; + + _caps_normal.set_active( _caps_all & SP_CSS_FONT_VARIANT_CAPS_NORMAL ); + _caps_small.set_active( _caps_all & SP_CSS_FONT_VARIANT_CAPS_SMALL ); + _caps_all_small.set_active( _caps_all & SP_CSS_FONT_VARIANT_CAPS_ALL_SMALL ); + _caps_petite.set_active( _caps_all & SP_CSS_FONT_VARIANT_CAPS_PETITE ); + _caps_all_petite.set_active( _caps_all & SP_CSS_FONT_VARIANT_CAPS_ALL_PETITE ); + _caps_unicase.set_active( _caps_all & SP_CSS_FONT_VARIANT_CAPS_UNICASE ); + _caps_titling.set_active( _caps_all & SP_CSS_FONT_VARIANT_CAPS_TITLING ); + + _caps_normal.set_inconsistent( _caps_mix & SP_CSS_FONT_VARIANT_CAPS_NORMAL ); + _caps_small.set_inconsistent( _caps_mix & SP_CSS_FONT_VARIANT_CAPS_SMALL ); + _caps_all_small.set_inconsistent( _caps_mix & SP_CSS_FONT_VARIANT_CAPS_ALL_SMALL ); + _caps_petite.set_inconsistent( _caps_mix & SP_CSS_FONT_VARIANT_CAPS_PETITE ); + _caps_all_petite.set_inconsistent( _caps_mix & SP_CSS_FONT_VARIANT_CAPS_ALL_PETITE ); + _caps_unicase.set_inconsistent( _caps_mix & SP_CSS_FONT_VARIANT_CAPS_UNICASE ); + _caps_titling.set_inconsistent( _caps_mix & SP_CSS_FONT_VARIANT_CAPS_TITLING ); + + _numeric_all = query->font_variant_numeric.computed; + _numeric_mix = query->font_variant_numeric.value; + + if (_numeric_all & SP_CSS_FONT_VARIANT_NUMERIC_LINING_NUMS) { + _numeric_lining.set_active(); + } else if (_numeric_all & SP_CSS_FONT_VARIANT_NUMERIC_OLDSTYLE_NUMS) { + _numeric_old_style.set_active(); + } else { + _numeric_default_style.set_active(); + } + + if (_numeric_all & SP_CSS_FONT_VARIANT_NUMERIC_PROPORTIONAL_NUMS) { + _numeric_proportional.set_active(); + } else if (_numeric_all & SP_CSS_FONT_VARIANT_NUMERIC_TABULAR_NUMS) { + _numeric_tabular.set_active(); + } else { + _numeric_default_width.set_active(); + } + + if (_numeric_all & SP_CSS_FONT_VARIANT_NUMERIC_DIAGONAL_FRACTIONS) { + _numeric_diagonal.set_active(); + } else if (_numeric_all & SP_CSS_FONT_VARIANT_NUMERIC_STACKED_FRACTIONS) { + _numeric_stacked.set_active(); + } else { + _numeric_default_fractions.set_active(); + } + + _numeric_ordinal.set_active( _numeric_all & SP_CSS_FONT_VARIANT_NUMERIC_ORDINAL ); + _numeric_slashed_zero.set_active( _numeric_all & SP_CSS_FONT_VARIANT_NUMERIC_SLASHED_ZERO ); + + + _numeric_lining.set_inconsistent( _numeric_mix & SP_CSS_FONT_VARIANT_NUMERIC_LINING_NUMS ); + _numeric_old_style.set_inconsistent( _numeric_mix & SP_CSS_FONT_VARIANT_NUMERIC_OLDSTYLE_NUMS ); + _numeric_proportional.set_inconsistent( _numeric_mix & SP_CSS_FONT_VARIANT_NUMERIC_PROPORTIONAL_NUMS ); + _numeric_tabular.set_inconsistent( _numeric_mix & SP_CSS_FONT_VARIANT_NUMERIC_TABULAR_NUMS ); + _numeric_diagonal.set_inconsistent( _numeric_mix & SP_CSS_FONT_VARIANT_NUMERIC_DIAGONAL_FRACTIONS ); + _numeric_stacked.set_inconsistent( _numeric_mix & SP_CSS_FONT_VARIANT_NUMERIC_STACKED_FRACTIONS ); + _numeric_ordinal.set_inconsistent( _numeric_mix & SP_CSS_FONT_VARIANT_NUMERIC_ORDINAL ); + _numeric_slashed_zero.set_inconsistent( _numeric_mix & SP_CSS_FONT_VARIANT_NUMERIC_SLASHED_ZERO ); + + if( query->font_feature_settings.value ) + _feature_entry.set_text( query->font_feature_settings.value ); + if( different_features ) { + _feature_label.show(); + } else { + _feature_label.hide(); + } + + + // Disable/Enable based on available OpenType tables. + font_instance* res = font_factory::Default()->FaceFromFontSpecification( font_spec.c_str() ); + if( res ) { + + std::map<Glib::ustring,int>::iterator it; + + if((it = res->openTypeTables.find("liga"))!= res->openTypeTables.end() || + (it = res->openTypeTables.find("clig"))!= res->openTypeTables.end()) { + _ligatures_common.set_sensitive(); + } else { + _ligatures_common.set_sensitive( false ); + } + + if((it = res->openTypeTables.find("dlig"))!= res->openTypeTables.end()) { + _ligatures_discretionary.set_sensitive(); + } else { + _ligatures_discretionary.set_sensitive( false ); + } + + if((it = res->openTypeTables.find("hlig"))!= res->openTypeTables.end()) { + _ligatures_historical.set_sensitive(); + } else { + _ligatures_historical.set_sensitive( false ); + } + + if((it = res->openTypeTables.find("calt"))!= res->openTypeTables.end()) { + _ligatures_contextual.set_sensitive(); + } else { + _ligatures_contextual.set_sensitive( false ); + } + + if((it = res->openTypeTables.find("subs"))!= res->openTypeTables.end()) { + _position_sub.set_sensitive(); + } else { + _position_sub.set_sensitive( false ); + } + + if((it = res->openTypeTables.find("sups"))!= res->openTypeTables.end()) { + _position_super.set_sensitive(); + } else { + _position_super.set_sensitive( false ); + } + + if((it = res->openTypeTables.find("smcp"))!= res->openTypeTables.end()) { + _caps_small.set_sensitive(); + } else { + _caps_small.set_sensitive( false ); + } + + if((it = res->openTypeTables.find("c2sc"))!= res->openTypeTables.end() && + (it = res->openTypeTables.find("smcp"))!= res->openTypeTables.end()) { + _caps_all_small.set_sensitive(); + } else { + _caps_all_small.set_sensitive( false ); + } + + if((it = res->openTypeTables.find("pcap"))!= res->openTypeTables.end()) { + _caps_petite.set_sensitive(); + } else { + _caps_petite.set_sensitive( false ); + } + + if((it = res->openTypeTables.find("c2sc"))!= res->openTypeTables.end() && + (it = res->openTypeTables.find("pcap"))!= res->openTypeTables.end()) { + _caps_all_petite.set_sensitive(); + } else { + _caps_all_petite.set_sensitive( false ); + } + + if((it = res->openTypeTables.find("unic"))!= res->openTypeTables.end()) { + _caps_unicase.set_sensitive(); + } else { + _caps_unicase.set_sensitive( false ); + } + + if((it = res->openTypeTables.find("titl"))!= res->openTypeTables.end()) { + _caps_titling.set_sensitive(); + } else { + _caps_titling.set_sensitive( false ); + } + + if((it = res->openTypeTables.find("lnum"))!= res->openTypeTables.end()) { + _numeric_lining.set_sensitive(); + } else { + _numeric_lining.set_sensitive( false ); + } + + if((it = res->openTypeTables.find("onum"))!= res->openTypeTables.end()) { + _numeric_old_style.set_sensitive(); + } else { + _numeric_old_style.set_sensitive( false ); + } + + if((it = res->openTypeTables.find("pnum"))!= res->openTypeTables.end()) { + _numeric_proportional.set_sensitive(); + } else { + _numeric_proportional.set_sensitive( false ); + } + + if((it = res->openTypeTables.find("tnum"))!= res->openTypeTables.end()) { + _numeric_tabular.set_sensitive(); + } else { + _numeric_tabular.set_sensitive( false ); + } + + if((it = res->openTypeTables.find("frac"))!= res->openTypeTables.end()) { + _numeric_diagonal.set_sensitive(); + } else { + _numeric_diagonal.set_sensitive( false ); + } + + if((it = res->openTypeTables.find("afrac"))!= res->openTypeTables.end()) { + _numeric_stacked.set_sensitive(); + } else { + _numeric_stacked.set_sensitive( false ); + } + + if((it = res->openTypeTables.find("ordn"))!= res->openTypeTables.end()) { + _numeric_ordinal.set_sensitive(); + } else { + _numeric_ordinal.set_sensitive( false ); + } + + if((it = res->openTypeTables.find("zero"))!= res->openTypeTables.end()) { + _numeric_slashed_zero.set_sensitive(); + } else { + _numeric_slashed_zero.set_sensitive( false ); + } + + // Make list of tables not handled above... eventually add Gtk::Label with + // this info. + // std::map<Glib::ustring,int> table_copy = res->openTypeTables; + // if( (it = table_copy.find("liga")) != table_copy.end() ) table_copy.erase( it ); + // if( (it = table_copy.find("clig")) != table_copy.end() ) table_copy.erase( it ); + // if( (it = table_copy.find("dlig")) != table_copy.end() ) table_copy.erase( it ); + // if( (it = table_copy.find("hlig")) != table_copy.end() ) table_copy.erase( it ); + // if( (it = table_copy.find("calt")) != table_copy.end() ) table_copy.erase( it ); + // if( (it = table_copy.find("subs")) != table_copy.end() ) table_copy.erase( it ); + // if( (it = table_copy.find("sups")) != table_copy.end() ) table_copy.erase( it ); + // if( (it = table_copy.find("smcp")) != table_copy.end() ) table_copy.erase( it ); + // if( (it = table_copy.find("c2sc")) != table_copy.end() ) table_copy.erase( it ); + // if( (it = table_copy.find("pcap")) != table_copy.end() ) table_copy.erase( it ); + // if( (it = table_copy.find("unic")) != table_copy.end() ) table_copy.erase( it ); + // if( (it = table_copy.find("titl")) != table_copy.end() ) table_copy.erase( it ); + // if( (it = table_copy.find("lnum")) != table_copy.end() ) table_copy.erase( it ); + // if( (it = table_copy.find("onum")) != table_copy.end() ) table_copy.erase( it ); + // if( (it = table_copy.find("pnum")) != table_copy.end() ) table_copy.erase( it ); + // if( (it = table_copy.find("tnum")) != table_copy.end() ) table_copy.erase( it ); + // if( (it = table_copy.find("frac")) != table_copy.end() ) table_copy.erase( it ); + // if( (it = table_copy.find("afrc")) != table_copy.end() ) table_copy.erase( it ); + // if( (it = table_copy.find("ordn")) != table_copy.end() ) table_copy.erase( it ); + // if( (it = table_copy.find("zero")) != table_copy.end() ) table_copy.erase( it ); + // for(it = table_copy.begin(); it != table_copy.end(); ++it) { + // std::cout << "Other: " << it->first << " Occurances: " << it->second << std::endl; + // } + + } else { + std::cerr << "FontVariants::update(): Couldn't find font_instance for: " + << font_spec << std::endl; + } + + + _ligatures_changed = false; + _position_changed = false; + _caps_changed = false; + _numeric_changed = false; + _feature_changed = false; + } + + void + FontVariants::fill_css( SPCSSAttr *css ) { + + // Ligatures + bool common = _ligatures_common.get_active(); + bool discretionary = _ligatures_discretionary.get_active(); + bool historical = _ligatures_historical.get_active(); + bool contextual = _ligatures_contextual.get_active(); + + if( !common && !discretionary && !historical && !contextual ) { + sp_repr_css_set_property(css, "font-variant-ligatures", "none" ); + } else if ( common && !discretionary && !historical && contextual ) { + sp_repr_css_set_property(css, "font-variant-ligatures", "normal" ); + } else { + Glib::ustring css_string; + if ( !common ) + css_string += "no-common-ligatures "; + if ( discretionary ) + css_string += "discretionary-ligatures "; + if ( historical ) + css_string += "historical-ligatures "; + if ( !contextual ) + css_string += "no-contextual "; + sp_repr_css_set_property(css, "font-variant-ligatures", css_string.c_str() ); + } + + // Position + { + unsigned position_new = SP_CSS_FONT_VARIANT_POSITION_NORMAL; + Glib::ustring css_string; + if( _position_normal.get_active() ) { + css_string = "normal"; + } else if( _position_sub.get_active() ) { + css_string = "sub"; + position_new = SP_CSS_FONT_VARIANT_POSITION_SUB; + } else if( _position_super.get_active() ) { + css_string = "super"; + position_new = SP_CSS_FONT_VARIANT_POSITION_SUPER; + } + + // 'if' may not be necessary... need to test. + if( (_position_all != position_new) || ((_position_mix != 0) && _position_changed) ) { + sp_repr_css_set_property(css, "font-variant-position", css_string.c_str() ); + } + } + + // Caps + { + unsigned caps_new = SP_CSS_FONT_VARIANT_CAPS_NORMAL; + Glib::ustring css_string; + if( _caps_normal.get_active() ) { + css_string = "normal"; + caps_new = SP_CSS_FONT_VARIANT_CAPS_NORMAL; + } else if( _caps_small.get_active() ) { + css_string = "small-caps"; + caps_new = SP_CSS_FONT_VARIANT_CAPS_SMALL; + } else if( _caps_all_small.get_active() ) { + css_string = "all-small-caps"; + caps_new = SP_CSS_FONT_VARIANT_CAPS_ALL_SMALL; + } else if( _caps_all_petite.get_active() ) { + css_string = "petite"; + caps_new = SP_CSS_FONT_VARIANT_CAPS_PETITE; + } else if( _caps_all_petite.get_active() ) { + css_string = "all-petite"; + caps_new = SP_CSS_FONT_VARIANT_CAPS_ALL_PETITE; + } else if( _caps_unicase.get_active() ) { + css_string = "unicase"; + caps_new = SP_CSS_FONT_VARIANT_CAPS_UNICASE; + } else if( _caps_titling.get_active() ) { + css_string = "titling"; + caps_new = SP_CSS_FONT_VARIANT_CAPS_TITLING; + } + + // May not be necessary... need to test. + //if( (_caps_all != caps_new) || ((_caps_mix != 0) && _caps_changed) ) { + sp_repr_css_set_property(css, "font-variant-caps", css_string.c_str() ); + //} + } + + // Numeric + bool default_style = _numeric_default_style.get_active(); + bool lining = _numeric_lining.get_active(); + bool old_style = _numeric_old_style.get_active(); + + bool default_width = _numeric_default_width.get_active(); + bool proportional = _numeric_proportional.get_active(); + bool tabular = _numeric_tabular.get_active(); + + bool default_fractions = _numeric_default_fractions.get_active(); + bool diagonal = _numeric_diagonal.get_active(); + bool stacked = _numeric_stacked.get_active(); + + bool ordinal = _numeric_ordinal.get_active(); + bool slashed_zero = _numeric_slashed_zero.get_active(); + + if (default_style & default_width & default_fractions & !ordinal & !slashed_zero) { + sp_repr_css_set_property(css, "font-variant-numeric", "normal"); + } else { + Glib::ustring css_string; + if ( lining ) + css_string += "lining-nums "; + if ( old_style ) + css_string += "oldstyle-nums "; + if ( proportional ) + css_string += "proportional-nums "; + if ( tabular ) + css_string += "tabular-nums "; + if ( diagonal ) + css_string += "diagonal-fractions "; + if ( stacked ) + css_string += "stacked-fractions "; + if ( ordinal ) + css_string += "ordinal "; + if ( slashed_zero ) + css_string += "slashed-zero "; + sp_repr_css_set_property(css, "font-variant-numeric", css_string.c_str() ); + } + + // Feature settings + Glib::ustring feature_string = _feature_entry.get_text(); + if( !feature_string.empty() || feature_string.compare( "normal" ) ) { + sp_repr_css_set_property(css, "font-feature-settings", feature_string.c_str()); + } + } + +} // namespace Widget +} // namespace UI +} // namespace Inkscape + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)) + 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/ui/widget/font-variants.h b/src/ui/widget/font-variants.h new file mode 100644 index 000000000..d4329feff --- /dev/null +++ b/src/ui/widget/font-variants.h @@ -0,0 +1,162 @@ +/* + * Author: + * Tavmjong Bah <tavmjong@free.fr> + * + * Copyright (C) 2015 Tavmong Bah + * + * Released under GNU GPL. Read the file 'COPYING' for more information. + */ + +#ifndef INKSCAPE_UI_WIDGET_FONT_VARIANT_H +#define INKSCAPE_UI_WIDGET_FONT_VARIANT_H + +#include <gtkmm/expander.h> +#include <gtkmm/checkbutton.h> +#include <gtkmm/radiobutton.h> +#include <gtkmm/entry.h> + +class SPDesktop; +class SPObject; +class SPStyle; +class SPCSSAttr; + +namespace Inkscape { +namespace UI { +namespace Widget { + +/** + * A container for selecting font variants (OpenType Features). + */ +class FontVariants : public Gtk::VBox +{ + +public: + + /** + * Constructor + */ + FontVariants(); + +protected: + // To start, use four check buttons. + Gtk::Expander _ligatures_frame; + Gtk::VBox _ligatures_vbox; + Gtk::CheckButton _ligatures_common; + Gtk::CheckButton _ligatures_discretionary; + Gtk::CheckButton _ligatures_historical; + Gtk::CheckButton _ligatures_contextual; + + // Exclusive options + Gtk::Expander _position_frame; + Gtk::VBox _position_vbox; + Gtk::RadioButton _position_normal; + Gtk::RadioButton _position_sub; + Gtk::RadioButton _position_super; + + // Exclusive options (maybe a dropdown menu to save space?) + Gtk::Expander _caps_frame; + Gtk::VBox _caps_vbox; + Gtk::RadioButton _caps_normal; + Gtk::RadioButton _caps_small; + Gtk::RadioButton _caps_all_small; + Gtk::RadioButton _caps_petite; + Gtk::RadioButton _caps_all_petite; + Gtk::RadioButton _caps_unicase; + Gtk::RadioButton _caps_titling; + + // Complicated! + Gtk::Expander _numeric_frame; + Gtk::VBox _numeric_vbox; + Gtk::HBox _numeric_stylebox; + Gtk::RadioButton _numeric_lining; + Gtk::RadioButton _numeric_old_style; + Gtk::RadioButton _numeric_default_style; + Gtk::HBox _numeric_widthbox; + Gtk::RadioButton _numeric_proportional; + Gtk::RadioButton _numeric_tabular; + Gtk::RadioButton _numeric_default_width; + Gtk::HBox _numeric_fractionbox; + Gtk::RadioButton _numeric_diagonal; + Gtk::RadioButton _numeric_stacked; + Gtk::RadioButton _numeric_default_fractions; + Gtk::CheckButton _numeric_ordinal; + Gtk::CheckButton _numeric_slashed_zero; + + Gtk::Expander _feature_frame; + Gtk::VBox _feature_vbox; + Gtk::Entry _feature_entry; + Gtk::Label _feature_label; + +private: + void ligatures_init(); + void ligatures_callback(); + + void position_init(); + void position_callback(); + + void caps_init(); + void caps_callback(); + + void numeric_init(); + void numeric_callback(); + + void feature_init(); + void feature_callback(); + + // To determine if we need to write out property (may not be necessary) + unsigned _ligatures_all; + unsigned _position_all; + unsigned _caps_all; + unsigned _numeric_all; + + unsigned _ligatures_mix; + unsigned _position_mix; + unsigned _caps_mix; + unsigned _numeric_mix; + + bool _ligatures_changed; + bool _position_changed; + bool _caps_changed; + bool _numeric_changed; + bool _feature_changed; + + sigc::signal<void> _changed_signal; + +public: + + /** + * Update GUI based on query results. + */ + void update( SPStyle const *query, bool different_features, Glib::ustring& font_spec ); + + /** + * Fill SPCSSAttr based on settings of buttons. + */ + void fill_css( SPCSSAttr* css ); + + /** + * Let others know that user has changed GUI settings. + * (Used to enable 'Apply' and 'Default' buttons.) + */ + sigc::connection connectChanged(sigc::slot<void> slot) { + return _changed_signal.connect(slot); + } +}; + + +} // namespace Widget +} // namespace UI +} // namespace Inkscape + +#endif // INKSCAPE_UI_WIDGET_FONT_VARIANT_H + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)) + 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/ui/widget/frame.h b/src/ui/widget/frame.h index 55638ad40..a04666651 100644 --- a/src/ui/widget/frame.h +++ b/src/ui/widget/frame.h @@ -10,14 +10,6 @@ #ifndef INKSCAPE_UI_WIDGET_FRAME_H #define INKSCAPE_UI_WIDGET_FRAME_H -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif - -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include <gtkmm/alignment.h> #include <gtkmm/frame.h> #include <gtkmm/label.h> diff --git a/src/ui/widget/imageicon.h b/src/ui/widget/imageicon.h index 8faf13cb1..2ea8b8533 100644 --- a/src/ui/widget/imageicon.h +++ b/src/ui/widget/imageicon.h @@ -12,14 +12,6 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif - -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include <gtkmm/box.h> #include <glibmm/ustring.h> diff --git a/src/ui/widget/imagetoggler.h b/src/ui/widget/imagetoggler.h index df6eb7ded..7b02fa4dc 100644 --- a/src/ui/widget/imagetoggler.h +++ b/src/ui/widget/imagetoggler.h @@ -14,10 +14,6 @@ #include "config.h" #endif -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include <gtkmm/cellrendererpixbuf.h> #include <gtkmm/widget.h> #include <glibmm/property.h> diff --git a/src/ui/widget/insertordericon.h b/src/ui/widget/insertordericon.h index fb3412d3f..bf8ac4fa7 100644 --- a/src/ui/widget/insertordericon.h +++ b/src/ui/widget/insertordericon.h @@ -13,10 +13,11 @@ # include "config.h" #endif -#include <glibmm.h> #include <gtkmm/cellrendererpixbuf.h> #include <gtkmm/widget.h> +#include <glibmm/property.h> + namespace Inkscape { namespace UI { namespace Widget { diff --git a/src/ui/widget/labelled.h b/src/ui/widget/labelled.h index 5334454bc..88eb3ce19 100644 --- a/src/ui/widget/labelled.h +++ b/src/ui/widget/labelled.h @@ -11,14 +11,6 @@ #ifndef INKSCAPE_UI_WIDGET_LABELLED_H #define INKSCAPE_UI_WIDGET_LABELLED_H -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif - -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include <gtkmm/box.h> namespace Gtk { diff --git a/src/ui/widget/layertypeicon.cpp b/src/ui/widget/layertypeicon.cpp index 3d6182bf8..672c607e5 100644 --- a/src/ui/widget/layertypeicon.cpp +++ b/src/ui/widget/layertypeicon.cpp @@ -11,10 +11,6 @@ # include <config.h> #endif -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -# include <glibmm/threads.h> -#endif - #include "ui/widget/layertypeicon.h" #include <gtkmm/icontheme.h> diff --git a/src/ui/widget/licensor.h b/src/ui/widget/licensor.h index c75c5fe9e..b96162589 100644 --- a/src/ui/widget/licensor.h +++ b/src/ui/widget/licensor.h @@ -10,14 +10,6 @@ #ifndef INKSCAPE_UI_WIDGET_LICENSOR_H #define INKSCAPE_UI_WIDGET_LICENSOR_H -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif - -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include <gtkmm/box.h> class SPDocument; diff --git a/src/ui/widget/notebook-page.h b/src/ui/widget/notebook-page.h index 4f7915423..c11de1b5b 100644 --- a/src/ui/widget/notebook-page.h +++ b/src/ui/widget/notebook-page.h @@ -14,10 +14,6 @@ # include <config.h> #endif -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include <gtkmm/box.h> namespace Gtk { diff --git a/src/ui/widget/object-composite-settings.cpp b/src/ui/widget/object-composite-settings.cpp index 00a74c4fe..8acf083d0 100644 --- a/src/ui/widget/object-composite-settings.cpp +++ b/src/ui/widget/object-composite-settings.cpp @@ -125,7 +125,8 @@ ObjectCompositeSettings::_blendBlurValueChanged() const Glib::ustring blendmode = _fe_cb.get_blend_mode(); //apply created filter to every selected item - for (StyleSubject::iterator i = _subject->begin() ; i != _subject->end() ; ++i ) { + std::vector<SPObject*> sel=_subject->list(); + for (std::vector<SPObject*>::const_iterator i = sel.begin() ; i != sel.end() ; ++i ) { if (!SP_IS_ITEM(*i)) { continue; } diff --git a/src/ui/widget/object-composite-settings.h b/src/ui/widget/object-composite-settings.h index 5a723a2fd..ae16564e1 100644 --- a/src/ui/widget/object-composite-settings.h +++ b/src/ui/widget/object-composite-settings.h @@ -11,14 +11,6 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif - -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include <gtkmm/box.h> #include <gtkmm/alignment.h> #include <gtkmm/adjustment.h> diff --git a/src/ui/widget/page-sizer.cpp b/src/ui/widget/page-sizer.cpp index 0a5697661..19ab1a280 100644 --- a/src/ui/widget/page-sizer.cpp +++ b/src/ui/widget/page-sizer.cpp @@ -266,12 +266,14 @@ PageSizer::PageSizer(Registry & _wr) _viewboxW.setDigits(2); _viewboxH.setDigits(2); + _dimensionWidth.setRange( 0.00001, 10000000 ); + _dimensionHeight.setRange( 0.00001, 10000000 ); _scaleX.setRange( 0.00001, 100000 ); _scaleY.setRange( 0.00001, 100000 ); _viewboxX.setRange( -100000, 100000 ); _viewboxY.setRange( -100000, 100000 ); - _viewboxW.setRange( 0, 200000 ); - _viewboxH.setRange( 0, 200000 ); + _viewboxW.setRange( 0.01, 200000 ); + _viewboxH.setRange( 0.01, 200000 ); _scaleY.set_sensitive (false); // We only want to display Y scale. diff --git a/src/ui/widget/panel.cpp b/src/ui/widget/panel.cpp index 0cff25d88..8a1e98a63 100644 --- a/src/ui/widget/panel.cpp +++ b/src/ui/widget/panel.cpp @@ -15,10 +15,6 @@ # include <config.h> #endif -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include <gtkmm/dialog.h> // for Gtk::RESPONSE_* #include <gtkmm/menu.h> #include <gtkmm/stock.h> diff --git a/src/ui/widget/panel.h b/src/ui/widget/panel.h index 5680cac30..a90060e17 100644 --- a/src/ui/widget/panel.h +++ b/src/ui/widget/panel.h @@ -17,10 +17,6 @@ # include <config.h> #endif -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include <gtkmm/box.h> #include <gtkmm/arrow.h> #include <gtkmm/button.h> diff --git a/src/ui/widget/preferences-widget.cpp b/src/ui/widget/preferences-widget.cpp index 98028ed78..72597e4d9 100644 --- a/src/ui/widget/preferences-widget.cpp +++ b/src/ui/widget/preferences-widget.cpp @@ -14,10 +14,6 @@ # include <config.h> #endif -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include <gtkmm/box.h> #include <gtkmm/frame.h> #include <gtkmm/alignment.h> diff --git a/src/ui/widget/preferences-widget.h b/src/ui/widget/preferences-widget.h index 5d9816e74..8b75b8368 100644 --- a/src/ui/widget/preferences-widget.h +++ b/src/ui/widget/preferences-widget.h @@ -22,10 +22,6 @@ #include <iostream> #include <vector> -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include <gtkmm/filechooserbutton.h> #include "ui/widget/spinbutton.h" #include <stddef.h> diff --git a/src/ui/widget/rotateable.cpp b/src/ui/widget/rotateable.cpp index 2d7597d7c..5e938dee6 100644 --- a/src/ui/widget/rotateable.cpp +++ b/src/ui/widget/rotateable.cpp @@ -11,10 +11,6 @@ # include "config.h" #endif -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include <gtkmm/box.h> #include <gtkmm/eventbox.h> #include <glibmm/i18n.h> diff --git a/src/ui/widget/selected-style.h b/src/ui/widget/selected-style.h index 0b6a14762..804a6fef6 100644 --- a/src/ui/widget/selected-style.h +++ b/src/ui/widget/selected-style.h @@ -15,10 +15,6 @@ # include "config.h" #endif -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include <gtkmm/box.h> #if WITH_GTKMM_3_0 diff --git a/src/ui/widget/spin-scale.h b/src/ui/widget/spin-scale.h index d0447e4a6..50e4fc953 100644 --- a/src/ui/widget/spin-scale.h +++ b/src/ui/widget/spin-scale.h @@ -13,10 +13,6 @@ # include <config.h> #endif -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include <gtkmm/adjustment.h> #include <gtkmm/box.h> #include <gtkmm/scale.h> diff --git a/src/ui/widget/spin-slider.h b/src/ui/widget/spin-slider.h index 74982ea58..a5999f14f 100644 --- a/src/ui/widget/spin-slider.h +++ b/src/ui/widget/spin-slider.h @@ -14,10 +14,6 @@ # include <config.h> #endif -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include <gtkmm/adjustment.h> #include <gtkmm/box.h> #include <gtkmm/scale.h> diff --git a/src/ui/widget/spinbutton.h b/src/ui/widget/spinbutton.h index cbe33e8ea..30ffc7d77 100644 --- a/src/ui/widget/spinbutton.h +++ b/src/ui/widget/spinbutton.h @@ -10,14 +10,6 @@ #ifndef INKSCAPE_UI_WIDGET_SPINBUTTON_H #define INKSCAPE_UI_WIDGET_SPINBUTTON_H -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif - -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include <gtkmm/spinbutton.h> namespace Inkscape { diff --git a/src/ui/widget/style-subject.cpp b/src/ui/widget/style-subject.cpp index a48370d9b..da3bbcd20 100644 --- a/src/ui/widget/style-subject.cpp +++ b/src/ui/widget/style-subject.cpp @@ -55,13 +55,11 @@ Inkscape::Selection *StyleSubject::Selection::_getSelection() const { } } -StyleSubject::iterator StyleSubject::Selection::begin() { +std::vector<SPObject*> StyleSubject::Selection::list(){ Inkscape::Selection *selection = _getSelection(); - if (selection) { - return iterator(selection->list()); - } else { - return iterator(NULL); - } + if(selection) + return selection->list(); + else return std::vector<SPObject*>(); } Geom::OptRect StyleSubject::Selection::getBounds(SPItem::BBoxType type) { @@ -104,8 +102,7 @@ void StyleSubject::Selection::setCSS(SPCSSAttr *css) { } StyleSubject::CurrentLayer::CurrentLayer() { - _element.data = NULL; - _element.next = NULL; + _element = NULL; } StyleSubject::CurrentLayer::~CurrentLayer() { @@ -114,10 +111,10 @@ StyleSubject::CurrentLayer::~CurrentLayer() { void StyleSubject::CurrentLayer::_setLayer(SPObject *layer) { _layer_release.disconnect(); _layer_modified.disconnect(); - if (_element.data) { - sp_object_unref(static_cast<SPObject *>(_element.data), NULL); + if (_element) { + sp_object_unref(_element, NULL); } - _element.data = layer; + _element = layer; if (layer) { sp_object_ref(layer, NULL); _layer_release = layer->connectRelease(sigc::hide(sigc::bind(sigc::mem_fun(*this, &CurrentLayer::_setLayer), (SPObject *)NULL))); @@ -127,19 +124,18 @@ void StyleSubject::CurrentLayer::_setLayer(SPObject *layer) { } SPObject *StyleSubject::CurrentLayer::_getLayer() const { - return static_cast<SPObject *>(_element.data); + return _element; } -GSList *StyleSubject::CurrentLayer::_getLayerSList() const { - if (_element.data) { - return &_element; - } else { - return NULL; - } +SPObject *StyleSubject::CurrentLayer::_getLayerSList() const { + return _element; + } -StyleSubject::iterator StyleSubject::CurrentLayer::begin() { - return iterator(_getLayerSList()); +std::vector<SPObject*> StyleSubject::CurrentLayer::list(){ + std::vector<SPObject*> list; + list.push_back(_element); + return list; } Geom::OptRect StyleSubject::CurrentLayer::getBounds(SPItem::BBoxType type) { @@ -152,8 +148,10 @@ Geom::OptRect StyleSubject::CurrentLayer::getBounds(SPItem::BBoxType type) { } int StyleSubject::CurrentLayer::queryStyle(SPStyle *query, int property) { - GSList *list = _getLayerSList(); - if (list) { + std::vector<SPItem*> list; + SPObject* i=_getLayerSList(); + if (i) { + list.push_back((SPItem*)i); return sp_desktop_query_style_from_list(list, query, property); } else { return QUERY_STYLE_NOTHING; diff --git a/src/ui/widget/style-subject.h b/src/ui/widget/style-subject.h index 47da91732..15a072f44 100644 --- a/src/ui/widget/style-subject.h +++ b/src/ui/widget/style-subject.h @@ -10,7 +10,6 @@ #ifndef SEEN_INKSCAPE_UI_WIDGET_STYLE_SUBJECT_H #define SEEN_INKSCAPE_UI_WIDGET_STYLE_SUBJECT_H -#include "util/glib-list-iterators.h" #include <boost/optional.hpp> #include <2geom/rect.h> #include "sp-item.h" @@ -35,7 +34,6 @@ public: class Selection; class CurrentLayer; - typedef Util::GSListConstIterator<SPObject *> iterator; StyleSubject(); virtual ~StyleSubject(); @@ -43,11 +41,10 @@ public: void setDesktop(SPDesktop *desktop); SPDesktop *getDesktop() const { return _desktop; } - virtual iterator begin() = 0; - virtual iterator end() { return iterator(NULL); } virtual Geom::OptRect getBounds(SPItem::BBoxType type) = 0; virtual int queryStyle(SPStyle *query, int property) = 0; virtual void setCSS(SPCSSAttr *css) = 0; + virtual std::vector<SPObject*> list(){return std::vector<SPObject*>();}; sigc::connection connectChanged(sigc::signal<void>::slot_type slot) { return _changed_signal.connect(slot); @@ -67,10 +64,10 @@ public: Selection(); ~Selection(); - virtual iterator begin(); virtual Geom::OptRect getBounds(SPItem::BBoxType type); virtual int queryStyle(SPStyle *query, int property); virtual void setCSS(SPCSSAttr *css); + virtual std::vector<SPObject*> list(); protected: virtual void _afterDesktopSwitch(SPDesktop *desktop); @@ -88,10 +85,10 @@ public: CurrentLayer(); ~CurrentLayer(); - virtual iterator begin(); virtual Geom::OptRect getBounds(SPItem::BBoxType type); virtual int queryStyle(SPStyle *query, int property); virtual void setCSS(SPCSSAttr *css); + virtual std::vector<SPObject*> list(); protected: virtual void _afterDesktopSwitch(SPDesktop *desktop); @@ -99,12 +96,12 @@ protected: private: SPObject *_getLayer() const; void _setLayer(SPObject *layer); - GSList *_getLayerSList() const; + SPObject *_getLayerSList() const; sigc::connection _layer_switched; sigc::connection _layer_release; sigc::connection _layer_modified; - mutable GSList _element; + mutable SPObject* _element; }; } diff --git a/src/ui/widget/style-swatch.h b/src/ui/widget/style-swatch.h index 582d2ebb3..0016e0256 100644 --- a/src/ui/widget/style-swatch.h +++ b/src/ui/widget/style-swatch.h @@ -17,10 +17,6 @@ # include "config.h" #endif -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include <gtkmm/box.h> #include <gtkmm/label.h> #include <gtkmm/eventbox.h> diff --git a/src/ui/widget/tolerance-slider.cpp b/src/ui/widget/tolerance-slider.cpp index ff525c679..ced811c57 100644 --- a/src/ui/widget/tolerance-slider.cpp +++ b/src/ui/widget/tolerance-slider.cpp @@ -12,10 +12,6 @@ # include <config.h> #endif -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include <gtkmm/adjustment.h> #include <gtkmm/box.h> #include <gtkmm/label.h> diff --git a/src/ui/widget/unit-menu.h b/src/ui/widget/unit-menu.h index 2fd25a6a9..f414660f7 100644 --- a/src/ui/widget/unit-menu.h +++ b/src/ui/widget/unit-menu.h @@ -10,14 +10,6 @@ #ifndef INKSCAPE_UI_WIDGET_UNIT_H #define INKSCAPE_UI_WIDGET_UNIT_H -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif - -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include <gtkmm/comboboxtext.h> #include "util/units.h" diff --git a/src/unclump.cpp b/src/unclump.cpp index 940369d7a..81c958937 100644 --- a/src/unclump.cpp +++ b/src/unclump.cpp @@ -168,13 +168,12 @@ unclump_dist (SPItem *item1, SPItem *item2) /** Average unclump_dist from item to others */ -static double unclump_average (SPItem *item, GSList *others) +static double unclump_average (SPItem *item, std::list<SPItem*> &others) { int n = 0; double sum = 0; - - for (GSList *i = others; i != NULL; i = i->next) { - SPItem *other = SP_ITEM (i->data); + for (std::list<SPItem*>::const_iterator i = others.begin(); i != others.end();i++) { + SPItem *other = *i; if (other == item) continue; @@ -192,13 +191,13 @@ static double unclump_average (SPItem *item, GSList *others) /** Closest to item among others */ -static SPItem *unclump_closest (SPItem *item, GSList *others) +static SPItem *unclump_closest (SPItem *item, std::list<SPItem*> &others) { double min = HUGE_VAL; SPItem *closest = NULL; - for (GSList *i = others; i != NULL; i = i->next) { - SPItem *other = SP_ITEM (i->data); + for (std::list<SPItem*>::const_iterator i = others.begin(); i != others.end();i++) { + SPItem *other = *i; if (other == item) continue; @@ -216,13 +215,12 @@ static SPItem *unclump_closest (SPItem *item, GSList *others) /** Most distant from item among others */ -static SPItem *unclump_farest (SPItem *item, GSList *others) +static SPItem *unclump_farest (SPItem *item, std::list<SPItem*> &others) { double max = -HUGE_VAL; SPItem *farest = NULL; - - for (GSList *i = others; i != NULL; i = i->next) { - SPItem *other = SP_ITEM (i->data); + for (std::list<SPItem*>::const_iterator i = others.begin(); i != others.end();i++) { + SPItem *other = *i; if (other == item) continue; @@ -242,8 +240,8 @@ Removes from the \a rest list those items that are "behind" \a closest as seen f i.e. those on the other side of the line through \a closest perpendicular to the direction from \a item to \a closest. Returns a newly created list which must be freed. */ -static GSList * -unclump_remove_behind (SPItem *item, SPItem *closest, GSList *rest) +static std::vector<SPItem*> +unclump_remove_behind (SPItem *item, SPItem *closest, std::list<SPItem*> &rest) { Geom::Point it = unclump_center (item); Geom::Point p1 = unclump_center (closest); @@ -260,10 +258,9 @@ unclump_remove_behind (SPItem *item, SPItem *closest, GSList *rest) // substitute the item into it: double val_item = A * it[Geom::X] + B * it[Geom::Y] + C; - GSList *out = NULL; - - for (GSList *i = rest; i != NULL; i = i->next) { - SPItem *other = SP_ITEM (i->data); + std::vector<SPItem*> out; + for (std::list<SPItem*>::const_reverse_iterator i = rest.rbegin(); i != rest.rend();i++) { + SPItem *other = *i; if (other == item) continue; @@ -274,7 +271,7 @@ unclump_remove_behind (SPItem *item, SPItem *closest, GSList *rest) if (val_item * val_other <= 1e-6) { // different signs, which means item and other are on the different sides of p1-p2 line; skip } else { - out = g_slist_prepend (out, other); + out.push_back(other); } } @@ -334,34 +331,34 @@ similar to "engraver dots". The only distribution which is unchanged by unclumpi grid. May be called repeatedly for stronger effect. */ void -unclump (GSList *items) +unclump (std::vector<SPItem*> &items) { c_cache.clear(); wh_cache.clear(); - for (GSList *i = items; i != NULL; i = i->next) { // for each original/clone x: - SPItem *item = SP_ITEM (i->data); + for (std::vector<SPItem*>::const_iterator i = items.begin(); i != items.end();i++) { // for each original/clone x: + SPItem *item = *i; - GSList *nei = NULL; + std::list<SPItem*> nei; - GSList *rest = g_slist_copy (items); - rest = g_slist_remove (rest, item); + std::list<SPItem*> rest; + for(int i=0;i<items.size();i++)rest.push_front(items[items.size()-i-1]); + rest.remove(item); - while (rest != NULL) { + while (!rest.empty()) { SPItem *closest = unclump_closest (item, rest); if (closest) { - nei = g_slist_prepend (nei, closest); - rest = g_slist_remove (rest, closest); - GSList *new_rest = unclump_remove_behind (item, closest, rest); - g_slist_free (rest); - rest = new_rest; + nei.push_front(closest); + rest.remove(closest); + std::vector<SPItem*> new_rest = unclump_remove_behind (item, closest, rest); + rest.clear(); + for(int i=0;i<new_rest.size();i++)rest.push_front(new_rest[new_rest.size()-i-1]); } else { - g_slist_free (rest); break; } } - if (g_slist_length (nei) >= 2) { + if ( (nei.size()) >= 2) { double ave = unclump_average (item, nei); SPItem *closest = unclump_closest (item, nei); diff --git a/src/unclump.h b/src/unclump.h index 2411cb713..461e99d0b 100644 --- a/src/unclump.h +++ b/src/unclump.h @@ -13,7 +13,7 @@ typedef struct _GSList GSList; -void unclump(GSList *items); +void unclump(std::vector<SPItem*> &items); #endif /* !UNCLUMP_H_SEEN */ diff --git a/src/uri-references.cpp b/src/uri-references.cpp index 2518c173e..04f904d39 100644 --- a/src/uri-references.cpp +++ b/src/uri-references.cpp @@ -43,6 +43,56 @@ URIReference::~URIReference() detach(); } +/* + * The main ideas here are: + * (1) "If we are inside a clone, then we can accept if and only if our "original thing" can accept the reference" + * (this caused problems when there are clones because a change in ids triggers signals for the object hrefing this id, but also its cloned reprs + * (descendants of <use> referencing an ancestor of the href'ing object)). The way it is done here is *atrocious*, but i could not find a better way. + * FIXME: find a better and safer way to find the "original object" of anyone with the flag ->cloned + * + * (2) Once we have an (potential owner) object, it can accept a href to obj, iff the graph of objects where directed edges are + * either parent->child relations , *** or href'ing to href'ed *** relations, stays acyclic. + * We can go either from owner and up in the tree, or from obj and down, in either case this will be in the worst case linear in the number of objects. + * There are no easy objects allowing to do the second proposition, while "hrefList" is a "list of objects href'ing us", so we'll take this. + * Then we keep a set of already visited elements, and do a DFS on this graph. if we find obj, then BOOM. + */ + +bool URIReference::_acceptObject(SPObject *obj) const { + //we go back following hrefList and parent to find if the object already references ourselves indirectly + std::set<SPObject*> done; + SPObject * owner = getOwner(); + if(!owner)return true; + while(owner->cloned){ + std::vector<int> positions; + while(owner->cloned){ + int position=0; + SPObject* c = owner->parent->firstChild(); + while(c != owner && dynamic_cast<SPObject*>(c) ){position++;c=c->next;} + positions.push_back(position); + owner=owner->parent; + } + owner = ((SPUse*)owner)->get_original(); + for(int i=positions.size()-2;i>=0;i--)owner=owner->childList(false)[positions[i]]; + } + //once we have the "original" object (hopefully) we look at who is referencing it + std::list<SPObject*> todo(owner->hrefList); + todo.push_front(owner->parent); + while(!todo.empty()){ + SPObject* e = todo.front(); + todo.pop_front(); + if(!dynamic_cast<SPObject*>(e))continue; + if(done.insert(e).second){ + if(e==obj){return false;} + todo.push_front(e->parent); + todo.insert(todo.begin(),e->hrefList.begin(),e->hrefList.end()); + } + } + return true; +} + + + + void URIReference::attach(const URI &uri) throw(BadURIException) { SPDocument *document = NULL; diff --git a/src/uri-references.h b/src/uri-references.h index 0c51481cc..e56ea0612 100644 --- a/src/uri-references.h +++ b/src/uri-references.h @@ -15,11 +15,15 @@ */ #include <cstddef> +#include <vector> +#include <set> #include <sigc++/connection.h> #include <sigc++/trackable.h> #include "bad-uri-exception.h" #include "sp-object.h" +#include "sp-item.h" +#include "sp-use.h" namespace Inkscape { @@ -122,11 +126,7 @@ public: SPObject *getOwnerObject() { return _owner; } protected: - virtual bool _acceptObject(SPObject *obj) const { - (void)obj; - return true; - } - + virtual bool _acceptObject(SPObject *obj) const; private: SPObject *_owner; SPDocument *_owner_document; diff --git a/src/util/ziptool.cpp b/src/util/ziptool.cpp index cf024008f..2eb516b2e 100644 --- a/src/util/ziptool.cpp +++ b/src/util/ziptool.cpp @@ -2605,7 +2605,7 @@ bool ZipFile::readFileData() if (gpBitFlag & 0x8)//bit 3 was set. means we dont know compressed size { unsigned char c1, c2, c3, c4; - c1 = c2 = c3 = c4 = 0; + c2 = c3 = c4 = 0; while (true) { unsigned char ch; diff --git a/src/vanishing-point.cpp b/src/vanishing-point.cpp index b62aacbc5..46f895c06 100644 --- a/src/vanishing-point.cpp +++ b/src/vanishing-point.cpp @@ -256,8 +256,9 @@ VanishingPoint::set_pos(Proj::Pt2 const &pt) { std::list<SPBox3D *> VanishingPoint::selectedBoxes(Inkscape::Selection *sel) { std::list<SPBox3D *> sel_boxes; - for (GSList const* i = sel->itemList(); i != NULL; i = i->next) { - SPItem *item = static_cast<SPItem *>(i->data); + std::vector<SPItem*> itemlist=sel->itemList(); + for (std::vector<SPItem*>::const_iterator i=itemlist.begin();i!=itemlist.end();i++) { + SPItem *item = *i; SPBox3D *box = dynamic_cast<SPBox3D *>(item); if (box && this->hasBox(box)) { sel_boxes.push_back(box); @@ -395,8 +396,9 @@ VPDragger::VPsOfSelectedBoxes() { VanishingPoint *vp; // FIXME: Should we take the selection from the parent VPDrag? I guess it shouldn't make a difference. Inkscape::Selection *sel = SP_ACTIVE_DESKTOP->getSelection(); - for (GSList const* i = sel->itemList(); i != NULL; i = i->next) { - SPItem *item = static_cast<SPItem *>(i->data); + std::vector<SPItem*> itemlist=sel->itemList(); + for (std::vector<SPItem*>::const_iterator i=itemlist.begin();i!=itemlist.end();i++) { + SPItem *item = *i; SPBox3D *box = dynamic_cast<SPBox3D *>(item); if (box) { vp = this->findVPWithBox(box); @@ -577,8 +579,9 @@ VPDrag::updateDraggers () g_return_if_fail (this->selection != NULL); - for (GSList const* i = this->selection->itemList(); i != NULL; i = i->next) { - SPItem *item = static_cast<SPItem *>(i->data); + std::vector<SPItem*> itemlist=this->selection->itemList(); + for (std::vector<SPItem*>::const_iterator i=itemlist.begin();i!=itemlist.end();i++) { + SPItem *item = *i; SPBox3D *box = dynamic_cast<SPBox3D *>(item); if (box) { VanishingPoint vp; @@ -609,8 +612,9 @@ VPDrag::updateLines () g_return_if_fail (this->selection != NULL); - for (GSList const* i = this->selection->itemList(); i != NULL; i = i->next) { - SPItem *item = static_cast<SPItem *>(i->data); + std::vector<SPItem*> itemlist=this->selection->itemList(); + for (std::vector<SPItem*>::const_iterator i=itemlist.begin();i!=itemlist.end();i++) { + SPItem *item = *i; SPBox3D *box = dynamic_cast<SPBox3D *>(item); if (box) { this->drawLinesForFace (box, Proj::X); @@ -626,11 +630,11 @@ VPDrag::updateBoxHandles () // FIXME: Is there a way to update the knots without accessing the // (previously) statically linked function KnotHolder::update_knots? - GSList *sel = (GSList *) selection->itemList(); - if (!sel) + std::vector<SPItem*> sel = selection->itemList(); + if (sel.empty()) return; // no selection - if (g_slist_length (sel) > 1) { + if (sel.size() > 1) { // Currently we only show handles if a single box is selected return; } diff --git a/src/verbs.cpp b/src/verbs.cpp index 5febeeb7c..e0ef27b0d 100644 --- a/src/verbs.cpp +++ b/src/verbs.cpp @@ -32,10 +32,9 @@ #include <cstring> #include <string> -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - +// Note that gtkmm headers must be included before gtk+ C headers +// in all files. The same applies for glibmm/glib etc. +// If this is not done, then errors will be generate relating to Glib::Threads being undefined #include <gtkmm/filechooserdialog.h> #include <gtkmm/messagedialog.h> #include <gtkmm/stock.h> @@ -1351,39 +1350,9 @@ void LayerVerb::perform(SPAction *action, void *data) } case SP_VERB_LAYER_DUPLICATE: { if ( dt->currentLayer() != dt->currentRoot() ) { - // Note with either approach: - // Any clone masters are duplicated, their clones use the *original*, - // but the duplicated master is not linked up as master nor clone of the original. -#if 0 - // Only copies selectable things, honoring locks, visibility, avoids sublayers. - SPObject *new_layer = Inkscape::create_layer(dt->currentRoot(), dt->currentLayer(), LPOS_BELOW); - if ( dt->currentLayer()->label() ) { - gchar* name = g_strdup_printf(_("%s copy"), dt->currentLayer()->label()); - dt->layer_manager->renameLayer( new_layer, name, TRUE ); - g_free(name); - } - sp_edit_select_all(dt); - sp_selection_duplicate(dt, true); - sp_selection_to_prev_layer(dt, true); - dt->setCurrentLayer(new_layer); - sp_edit_select_all(dt); -#else - // Copies everything, regardless of locks, visibility, sublayers. - //XML Tree being directly used here while it shouldn't be. - Inkscape::XML::Node *selected = dt->currentLayer()->getRepr(); - Inkscape::XML::Node *parent = selected->parent(); - Inkscape::XML::Node *dup = selected->duplicate(parent->document()); - parent->addChild(dup, selected); - SPObject *new_layer = dt->currentLayer()->next; - if (new_layer) { - if (new_layer->label()) { - gchar* name = g_strdup_printf(_("%s copy"), new_layer->label()); - dt->layer_manager->renameLayer( new_layer, name, TRUE ); - g_free(name); - } - dt->setCurrentLayer(new_layer); - } -#endif + + sp_selection_duplicate(dt, true, true); + DocumentUndo::done(dt->getDocument(), SP_VERB_LAYER_DUPLICATE, _("Duplicate layer")); diff --git a/src/widgets/CMakeLists.txt b/src/widgets/CMakeLists.txt index 072b905a2..a3e9e14d0 100644 --- a/src/widgets/CMakeLists.txt +++ b/src/widgets/CMakeLists.txt @@ -41,12 +41,7 @@ set(widgets_SRC select-toolbar.cpp shrink-wrap-button.cpp sp-attribute-widget.cpp - sp-color-icc-selector.cpp - sp-color-notebook.cpp - sp-color-scales.cpp sp-color-selector.cpp - sp-color-slider.cpp - sp-color-wheel-selector.cpp sp-widget.cpp sp-xmlview-attr-list.cpp sp-xmlview-content.cpp @@ -103,12 +98,7 @@ set(widgets_SRC select-toolbar.h shrink-wrap-button.h sp-attribute-widget.h - sp-color-icc-selector.h - sp-color-notebook.h - sp-color-scales.h sp-color-selector.h - sp-color-slider.h - sp-color-wheel-selector.h sp-widget.h sp-xmlview-attr-list.h sp-xmlview-content.h diff --git a/src/widgets/Makefile_insert b/src/widgets/Makefile_insert index dc4c12967..f66be66ed 100644 --- a/src/widgets/Makefile_insert +++ b/src/widgets/Makefile_insert @@ -76,18 +76,8 @@ ink_common_sources += \ widgets/spiral-toolbar.h \ widgets/sp-attribute-widget.cpp \ widgets/sp-attribute-widget.h \ - widgets/sp-color-icc-selector.cpp \ - widgets/sp-color-icc-selector.h \ - widgets/sp-color-notebook.cpp \ - widgets/sp-color-notebook.h \ - widgets/sp-color-scales.cpp \ - widgets/sp-color-scales.h \ widgets/sp-color-selector.cpp \ widgets/sp-color-selector.h \ - widgets/sp-color-slider.cpp \ - widgets/sp-color-slider.h \ - widgets/sp-color-wheel-selector.cpp \ - widgets/sp-color-wheel-selector.h \ widgets/spinbutton-events.cpp \ widgets/spinbutton-events.h \ widgets/sp-widget.cpp \ diff --git a/src/widgets/arc-toolbar.cpp b/src/widgets/arc-toolbar.cpp index 8a64854be..71418e238 100644 --- a/src/widgets/arc-toolbar.cpp +++ b/src/widgets/arc-toolbar.cpp @@ -97,12 +97,9 @@ sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *v gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL); bool modmade = false; - for (GSList const *items = desktop->getSelection()->itemList(); - items != NULL; - items = items->next) - { - SPItem *item = SP_ITEM(items->data); - + std::vector<SPItem*> itemlist=desktop->getSelection()->itemList(); + for(std::vector<SPItem*>::const_iterator i=itemlist.begin();i!=itemlist.end();i++){ + SPItem *item = *i; if (SP_IS_GENERICELLIPSE(item)) { SPGenericEllipse *ge = SP_GENERICELLIPSE(item); @@ -166,11 +163,9 @@ static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl ) bool modmade = false; if ( ege_select_one_action_get_active(act) != 0 ) { - for (GSList const *items = desktop->getSelection()->itemList(); - items != NULL; - items = items->next) - { - SPItem *item = reinterpret_cast<SPItem*>(items->data); + std::vector<SPItem*> itemlist=desktop->getSelection()->itemList(); + for(std::vector<SPItem*>::const_iterator i=itemlist.begin();i!=itemlist.end();i++){ + SPItem *item = *i; if (SP_IS_GENERICELLIPSE(item)) { Inkscape::XML::Node *repr = item->getRepr(); repr->setAttribute("sodipodi:open", "true"); @@ -179,11 +174,9 @@ static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl ) } } } else { - for (GSList const *items = desktop->getSelection()->itemList(); - items != NULL; - items = items->next) - { - SPItem *item = reinterpret_cast<SPItem *>(items->data); + std::vector<SPItem*> itemlist=desktop->getSelection()->itemList(); + for(std::vector<SPItem*>::const_iterator i=itemlist.begin();i!=itemlist.end();i++){ + SPItem *item = *i; if (SP_IS_GENERICELLIPSE(item)) { Inkscape::XML::Node *repr = item->getRepr(); repr->setAttribute("sodipodi:open", NULL); @@ -271,11 +264,9 @@ static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GOb purge_repr_listener( tbl, tbl ); - for (GSList const *items = selection->itemList(); - items != NULL; - items = items->next) - { - SPItem *item = reinterpret_cast<SPItem *>(items->data); + std::vector<SPItem*> itemlist=selection->itemList(); + for(std::vector<SPItem*>::const_iterator i=itemlist.begin();i!=itemlist.end();i++){ + SPItem *item = *i; if (SP_IS_GENERICELLIPSE(item)) { n_selected++; repr = item->getRepr(); diff --git a/src/widgets/connector-toolbar.cpp b/src/widgets/connector-toolbar.cpp index c906f7de4..1c99f283d 100644 --- a/src/widgets/connector-toolbar.cpp +++ b/src/widgets/connector-toolbar.cpp @@ -76,7 +76,6 @@ static void sp_connector_path_set_ignore(void) static void sp_connector_orthogonal_toggled( GtkToggleAction* act, GObject *tbl ) { SPDesktop *desktop = static_cast<SPDesktop *>(g_object_get_data( tbl, "desktop" )); - Inkscape::Selection * selection = desktop->getSelection(); SPDocument *doc = desktop->getDocument(); if (!DocumentUndo::getUndoSensitive(doc)) { @@ -98,9 +97,9 @@ static void sp_connector_orthogonal_toggled( GtkToggleAction* act, GObject *tbl gchar *value = is_orthog ? orthog_str : polyline_str ; bool modmade = false; - GSList *l = (GSList *) selection->itemList(); - while (l) { - SPItem *item = SP_ITEM(l->data); + std::vector<SPItem*> itemlist=desktop->getSelection()->itemList(); + for(std::vector<SPItem*>::const_iterator i=itemlist.begin();i!=itemlist.end();i++){ + SPItem *item = *i; if (Inkscape::UI::Tools::cc_item_is_connector(item)) { item->setAttribute( "inkscape:connector-type", @@ -108,7 +107,6 @@ static void sp_connector_orthogonal_toggled( GtkToggleAction* act, GObject *tbl item->avoidRef->handleSettingChange(); modmade = true; } - l = l->next; } if (!modmade) { @@ -126,7 +124,6 @@ static void sp_connector_orthogonal_toggled( GtkToggleAction* act, GObject *tbl static void connector_curvature_changed(GtkAdjustment *adj, GObject* tbl) { SPDesktop *desktop = static_cast<SPDesktop *>(g_object_get_data( tbl, "desktop" )); - Inkscape::Selection * selection = desktop->getSelection(); SPDocument *doc = desktop->getDocument(); if (!DocumentUndo::getUndoSensitive(doc)) { @@ -147,9 +144,9 @@ static void connector_curvature_changed(GtkAdjustment *adj, GObject* tbl) g_ascii_dtostr(value, G_ASCII_DTOSTR_BUF_SIZE, newValue); bool modmade = false; - GSList *l = (GSList *) selection->itemList(); - while (l) { - SPItem *item = SP_ITEM(l->data); + std::vector<SPItem*> itemlist=desktop->getSelection()->itemList(); + for(std::vector<SPItem*>::const_iterator i=itemlist.begin();i!=itemlist.end();i++){ + SPItem *item = *i; if (Inkscape::UI::Tools::cc_item_is_connector(item)) { item->setAttribute( "inkscape:connector-curvature", @@ -157,7 +154,6 @@ static void connector_curvature_changed(GtkAdjustment *adj, GObject* tbl) item->avoidRef->handleSettingChange(); modmade = true; } - l = l->next; } if (!modmade) { diff --git a/src/widgets/dash-selector.h b/src/widgets/dash-selector.h index 5b9ebfa2b..ec5a1cbd5 100644 --- a/src/widgets/dash-selector.h +++ b/src/widgets/dash-selector.h @@ -14,10 +14,6 @@ # include <config.h> #endif -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include <gtkmm/box.h> #include <gtkmm/combobox.h> #include <gtkmm/liststore.h> diff --git a/src/widgets/desktop-widget.cpp b/src/widgets/desktop-widget.cpp index fd3756220..e19f56e48 100644 --- a/src/widgets/desktop-widget.cpp +++ b/src/widgets/desktop-widget.cpp @@ -1433,8 +1433,10 @@ sp_desktop_widget_maximize(SPDesktopWidget *dtw) if (!dtw->desktop->is_iconified() && !dtw->desktop->is_fullscreen()) { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - gint w, h, x, y; + gint w = -1; + gint h, x, y; dtw->getWindowGeometry(x, y, w, h); + g_assert(w != -1); prefs->setInt("/desktop/geometry/width", w); prefs->setInt("/desktop/geometry/height", h); prefs->setInt("/desktop/geometry/x", x); diff --git a/src/widgets/desktop-widget.h b/src/widgets/desktop-widget.h index a77d56fc3..489217d9a 100644 --- a/src/widgets/desktop-widget.h +++ b/src/widgets/desktop-widget.h @@ -12,14 +12,6 @@ * ? -2004 */ -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif - -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include <gtkmm/window.h> #include "message.h" #include "ui/view/view-widget.h" diff --git a/src/widgets/ege-adjustment-action.cpp b/src/widgets/ege-adjustment-action.cpp index d89a6e3f1..a91149f4c 100644 --- a/src/widgets/ege-adjustment-action.cpp +++ b/src/widgets/ege-adjustment-action.cpp @@ -742,7 +742,7 @@ static GtkWidget* create_popup_number_menu( EgeAdjustmentAction* act ) if ( act->private_data->descriptions ) { gdouble value = ((EgeAdjustmentDescr*)act->private_data->descriptions->data)->value; - addOns = flush_explicit_items( addOns, G_CALLBACK(process_menu_action), BUMP_CUSTOM, menu, act, &single, &group, value ); + flush_explicit_items( addOns, G_CALLBACK(process_menu_action), BUMP_CUSTOM, menu, act, &single, &group, value ); } return menu; diff --git a/src/widgets/fill-style.cpp b/src/widgets/fill-style.cpp index d60a92b8b..fa5eabab4 100644 --- a/src/widgets/fill-style.cpp +++ b/src/widgets/fill-style.cpp @@ -22,10 +22,6 @@ # include "config.h" #endif -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include <gtkmm/box.h> #include <glibmm/i18n.h> @@ -304,7 +300,7 @@ void FillNStroke::performUpdate() psel->setGradientProperties( rg->getUnits(), rg->getSpread() ); } else if (SP_IS_PATTERN(server)) { - SPPattern *pat = pattern_getroot(SP_PATTERN(server)); + SPPattern *pat = SP_PATTERN(server)->rootPattern(); psel->updatePatternList( pat ); } } @@ -435,8 +431,7 @@ void FillNStroke::dragFromPaint() update = true; switch (psel->mode) { - case SPPaintSelector::MODE_COLOR_RGB: - case SPPaintSelector::MODE_COLOR_CMYK: + case SPPaintSelector::MODE_SOLID_COLOR: { // local change, do not update from selection dragId = g_timeout_add_full(G_PRIORITY_DEFAULT, 100, dragDelayCB, this, 0); @@ -481,7 +476,7 @@ void FillNStroke::updateFromPaint() SPDocument *document = desktop->getDocument(); Inkscape::Selection *selection = desktop->getSelection(); - GSList const *items = selection->itemList(); + std::vector<SPItem*> const items = selection->itemList(); switch (psel->mode) { case SPPaintSelector::MODE_EMPTY: @@ -509,8 +504,7 @@ void FillNStroke::updateFromPaint() break; } - case SPPaintSelector::MODE_COLOR_RGB: - case SPPaintSelector::MODE_COLOR_CMYK: + case SPPaintSelector::MODE_SOLID_COLOR: { if (kind == FILL) { // FIXME: fix for GTK breakage, see comment in SelectedStyle::on_opacity_changed; here it results in losing release events @@ -543,7 +537,7 @@ void FillNStroke::updateFromPaint() case SPPaintSelector::MODE_GRADIENT_LINEAR: case SPPaintSelector::MODE_GRADIENT_RADIAL: case SPPaintSelector::MODE_SWATCH: - if (items) { + if (!items.empty()) { SPGradientType const gradient_type = ( psel->mode != SPPaintSelector::MODE_GRADIENT_RADIAL ? SP_GRADIENT_TYPE_LINEAR : SP_GRADIENT_TYPE_RADIAL ); @@ -561,7 +555,7 @@ void FillNStroke::updateFromPaint() /* No vector in paint selector should mean that we just changed mode */ SPStyle query(desktop->doc()); - int result = objects_query_fillstroke(const_cast<GSList *>(items), &query, kind == FILL); + int result = objects_query_fillstroke(items, &query, kind == FILL); if (result == QUERY_STYLE_MULTIPLE_SAME) { SPIPaint &targPaint = (kind == FILL) ? query.fill : query.stroke; SPColor common; @@ -576,39 +570,39 @@ void FillNStroke::updateFromPaint() } } - for (GSList const *i = items; i != NULL; i = i->next) { + for(std::vector<SPItem*>::const_iterator i=items.begin();i!=items.end();i++){ //FIXME: see above if (kind == FILL) { - sp_repr_css_change_recursive(reinterpret_cast<SPObject*>(i->data)->getRepr(), css, "style"); + sp_repr_css_change_recursive((*i)->getRepr(), css, "style"); } if (!vector) { SPGradient *gr = sp_gradient_vector_for_object( document, desktop, - reinterpret_cast<SPObject*>(i->data), + reinterpret_cast<SPObject*>(*i), (kind == FILL) ? Inkscape::FOR_FILL : Inkscape::FOR_STROKE, createSwatch ); if ( gr && createSwatch ) { gr->setSwatch(); } - sp_item_set_gradient(SP_ITEM(i->data), + sp_item_set_gradient(*i, gr, gradient_type, (kind == FILL) ? Inkscape::FOR_FILL : Inkscape::FOR_STROKE); } else { - sp_item_set_gradient(SP_ITEM(i->data), vector, gradient_type, (kind == FILL) ? Inkscape::FOR_FILL : Inkscape::FOR_STROKE); + sp_item_set_gradient(*i, vector, gradient_type, (kind == FILL) ? Inkscape::FOR_FILL : Inkscape::FOR_STROKE); } } } else { // We have changed from another gradient type, or modified spread/units within // this gradient type. vector = sp_gradient_ensure_vector_normalized(vector); - for (GSList const *i = items; i != NULL; i = i->next) { + for(std::vector<SPItem*>::const_iterator i=items.begin();i!=items.end();i++){ //FIXME: see above if (kind == FILL) { - sp_repr_css_change_recursive(reinterpret_cast<SPObject*>(i->data)->getRepr(), css, "style"); + sp_repr_css_change_recursive((*i)->getRepr(), css, "style"); } - SPGradient *gr = sp_item_set_gradient(SP_ITEM(i->data), vector, gradient_type, (kind == FILL) ? Inkscape::FOR_FILL : Inkscape::FOR_STROKE); + SPGradient *gr = sp_item_set_gradient(*i, vector, gradient_type, (kind == FILL) ? Inkscape::FOR_FILL : Inkscape::FOR_STROKE); psel->pushAttrsToGradient( gr ); } } @@ -625,7 +619,7 @@ void FillNStroke::updateFromPaint() case SPPaintSelector::MODE_PATTERN: - if (items) { + if (!items.empty()) { SPPattern *pattern = psel->getPattern(); if (!pattern) { @@ -648,19 +642,19 @@ void FillNStroke::updateFromPaint() // cannot just call sp_desktop_set_style, because we don't want to touch those // objects who already have the same root pattern but through a different href // chain. FIXME: move this to a sp_item_set_pattern - for (GSList const *i = items; i != NULL; i = i->next) { - Inkscape::XML::Node *selrepr = reinterpret_cast<SPObject*>(i->data)->getRepr(); + for(std::vector<SPItem*>::const_iterator i=items.begin();i!=items.end();i++){ + Inkscape::XML::Node *selrepr = (*i)->getRepr(); if ( (kind == STROKE) && !selrepr) { continue; } - SPObject *selobj = reinterpret_cast<SPObject*>(i->data); + SPObject *selobj = *i; SPStyle *style = selobj->style; if (style && ((kind == FILL) ? style->fill : style->stroke).isPaintserver()) { SPPaintServer *server = (kind == FILL) ? selobj->style->getFillPaintServer() : selobj->style->getStrokePaintServer(); - if (SP_IS_PATTERN(server) && pattern_getroot(SP_PATTERN(server)) == pattern) + if (SP_IS_PATTERN(server) && SP_PATTERN(server)->rootPattern() == pattern) // only if this object's pattern is not rooted in our selected pattern, apply continue; } @@ -686,7 +680,7 @@ void FillNStroke::updateFromPaint() break; case SPPaintSelector::MODE_UNSET: - if (items) { + if (!items.empty()) { SPCSSAttr *css = sp_repr_css_attr_new(); if (kind == FILL) { sp_repr_css_unset_property(css, "fill"); diff --git a/src/widgets/gradient-selector.cpp b/src/widgets/gradient-selector.cpp index 42e59cbfe..402f30846 100644 --- a/src/widgets/gradient-selector.cpp +++ b/src/widgets/gradient-selector.cpp @@ -17,6 +17,10 @@ # include "config.h" #endif +#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H +#include <glibmm/threads.h> +#endif + #include <gtkmm/treeview.h> #include "gradient-vector.h" diff --git a/src/widgets/gradient-toolbar.cpp b/src/widgets/gradient-toolbar.cpp index 7ce04403b..6743dd23a 100644 --- a/src/widgets/gradient-toolbar.cpp +++ b/src/widgets/gradient-toolbar.cpp @@ -116,8 +116,9 @@ void gr_apply_gradient(Inkscape::Selection *selection, GrDrag *drag, SPGradient } // If no drag or no dragger selected, act on selection - for (GSList const* i = selection->itemList(); i != NULL; i = i->next) { - gr_apply_gradient_to_item(SP_ITEM(i->data), gr, initialType, initialMode, initialMode); + std::vector<SPItem*> itemlist=selection->itemList(); + for(std::vector<SPItem*>::const_iterator i=itemlist.begin();i!=itemlist.end();i++){ + gr_apply_gradient_to_item(*i, gr, initialType, initialMode, initialMode); } } @@ -217,8 +218,9 @@ void gr_get_dt_selected_gradient(Inkscape::Selection *selection, SPGradient *&gr { SPGradient *gradient = 0; - for (GSList const* i = selection->itemList(); i; i = i->next) { - SPItem *item = SP_ITEM(i->data); // get the items gradient, not the getVector() version + std::vector<SPItem*> itemlist=selection->itemList(); + for(std::vector<SPItem*>::const_iterator i=itemlist.begin();i!=itemlist.end();i++){ + SPItem *item = *i;// get the items gradient, not the getVector() version SPStyle *style = item->style; SPPaintServer *server = 0; @@ -284,8 +286,9 @@ void gr_read_selection( Inkscape::Selection *selection, } // If no selected dragger, read desktop selection - for (GSList const* i = selection->itemList(); i; i = i->next) { - SPItem *item = SP_ITEM(i->data); + std::vector<SPItem*> itemlist=selection->itemList(); + for(std::vector<SPItem*>::const_iterator i=itemlist.begin();i!=itemlist.end();i++){ + SPItem *item = *i; SPStyle *style = item->style; if (style && (style->fill.isPaintserver())) { diff --git a/src/widgets/gradient-vector.cpp b/src/widgets/gradient-vector.cpp index d2c46ffec..259d4c9af 100644 --- a/src/widgets/gradient-vector.cpp +++ b/src/widgets/gradient-vector.cpp @@ -31,17 +31,16 @@ #include "macros.h" #include <glibmm/i18n.h> #include <set> -#include "widgets/gradient-image.h" -#include "inkscape.h" -#include "document-private.h" -#include "gradient-chemistry.h" -#include "helper/window.h" +#include "../widgets/gradient-image.h" +#include "../inkscape.h" +#include "../document-private.h" +#include "../gradient-chemistry.h" +#include "../helper/window.h" #include "io/resource.h" #include "xml/repr.h" -#include "ui/dialog-events.h" -#include "preferences.h" +#include "../preferences.h" #include "svg/css-ostringstream.h" #include "sp-stop.h" #include "selection-chemistry.h" @@ -51,10 +50,16 @@ #include "desktop.h" #include "layer-manager.h" -#include <sigc++/sigc++.h> +#include <sigc++/functors/ptr_fun.h> +#include <sigc++/adaptors/bind.h> #include "document-undo.h" +#include "ui/dialog-events.h" +#include "ui/selected-color.h" +#include "ui/widget/color-notebook.h" + using Inkscape::DocumentUndo; +using Inkscape::UI::SelectedColor; enum { VECTOR_SET, @@ -468,11 +473,10 @@ void SPGradientVectorSelector::setSwatched() ### Vector Editing Widget ##################################################################*/ -#include "widgets/sp-color-notebook.h" #include "widgets/widget-sizes.h" #include "xml/node-event-vector.h" #include "svg/svg-color.h" - +#include "ui/widget/color-notebook.h" #define PAD 4 @@ -491,8 +495,8 @@ static void sp_gradient_vector_widget_destroy(GtkObject *object, gpointer data); static void sp_gradient_vector_gradient_release(SPObject *obj, GtkWidget *widget); static void sp_gradient_vector_gradient_modified(SPObject *obj, guint flags, GtkWidget *widget); -static void sp_gradient_vector_color_dragged(SPColorSelector *csel, GObject *object); -static void sp_gradient_vector_color_changed(SPColorSelector *csel, GObject *object); +static void sp_gradient_vector_color_dragged(Inkscape::UI::SelectedColor *selected_color, GObject *object); +static void sp_gradient_vector_color_changed(Inkscape::UI::SelectedColor *selected_color, GObject *object); static void update_stop_list( GtkWidget *vb, SPGradient *gradient, SPStop *new_stop); static gboolean blocked = FALSE; @@ -548,6 +552,7 @@ static void verify_grad(SPGradient *gradient) child->setAttribute("style", os.str().c_str()); gradient->getRepr()->addChild(child, NULL); Inkscape::GC::release(child); + return; } if (i < 2) { sp_repr_set_css_double(stop->getRepr(), "offset", 0.0); @@ -648,9 +653,11 @@ static void sp_grad_edit_combo_box_changed (GtkComboBox * /*widget*/, GtkWidget blocked = TRUE; - SPColorSelector *csel = static_cast<SPColorSelector*>(g_object_get_data(G_OBJECT(tbl), "cselector")); + SelectedColor *csel = static_cast<SelectedColor*>(g_object_get_data(G_OBJECT(tbl), "cselector")); // set its color, from the stored array - csel->base->setColorAlpha( stop->getEffectiveColor(), stop->opacity ); + g_object_set_data(G_OBJECT(tbl), "updating_color", reinterpret_cast<void*>(1)); + csel->setColorAlpha(stop->getEffectiveColor(), stop->opacity); + g_object_set_data(G_OBJECT(tbl), "updating_color", reinterpret_cast<void*>(0)); GtkWidget *offspin = GTK_WIDGET(g_object_get_data(G_OBJECT(tbl), "offspn")); GtkWidget *offslide =GTK_WIDGET(g_object_get_data(G_OBJECT(tbl), "offslide")); @@ -831,7 +838,9 @@ static void sp_grd_ed_del_stop(GtkWidget */*widget*/, GtkWidget *vb) static GtkWidget * sp_gradient_vector_widget_new(SPGradient *gradient, SPStop *select_stop) { - GtkWidget *vb, *w, *f, *csel; + using Inkscape::UI::Widget::ColorNotebook; + + GtkWidget *vb, *w, *f; g_return_val_if_fail(!gradient || SP_IS_GRADIENT(gradient), NULL); @@ -959,12 +968,23 @@ static GtkWidget * sp_gradient_vector_widget_new(SPGradient *gradient, SPStop *s f = gtk_frame_new(_("Stop Color")); gtk_widget_show(f); gtk_box_pack_start(GTK_BOX(vb), f, TRUE, TRUE, PAD); - csel = static_cast<GtkWidget*>(sp_color_selector_new(SP_TYPE_COLOR_NOTEBOOK)); - g_object_set_data(G_OBJECT(vb), "cselector", csel); + + Inkscape::UI::SelectedColor *selected_color = new Inkscape::UI::SelectedColor; + g_object_set_data(G_OBJECT(vb), "cselector", selected_color); + g_object_set_data(G_OBJECT(vb), "updating_color", reinterpret_cast<void*>(0)); + selected_color->signal_dragged.connect(sigc::bind(sigc::ptr_fun(&sp_gradient_vector_color_dragged), selected_color, G_OBJECT(vb))); + selected_color->signal_dragged.connect(sigc::bind(sigc::ptr_fun(&sp_gradient_vector_color_changed), selected_color, G_OBJECT(vb))); + + Gtk::Widget *color_selector = Gtk::manage(new ColorNotebook(*selected_color)); + color_selector->show(); + gtk_container_add(GTK_CONTAINER(f), color_selector->gobj()); + + /* gtk_widget_show(csel); gtk_container_add(GTK_CONTAINER(f), csel); g_signal_connect(G_OBJECT(csel), "dragged", G_CALLBACK(sp_gradient_vector_color_dragged), vb); g_signal_connect(G_OBJECT(csel), "changed", G_CALLBACK(sp_gradient_vector_color_changed), vb); + */ gtk_widget_show(vb); @@ -1125,9 +1145,11 @@ static void sp_gradient_vector_widget_load_gradient(GtkWidget *widget, SPGradien } // get the color selector - SPColorSelector *csel = SP_COLOR_SELECTOR(g_object_get_data(G_OBJECT(widget), "cselector")); + SelectedColor *csel = static_cast<SelectedColor*>(g_object_get_data(G_OBJECT(widget), "cselector")); - csel->base->setColorAlpha( stop->getEffectiveColor(), stop->opacity ); + g_object_set_data(G_OBJECT(widget), "updating_color", reinterpret_cast<void*>(1)); + csel->setColorAlpha(stop->getEffectiveColor(), stop->opacity); + g_object_set_data(G_OBJECT(widget), "updating_color", reinterpret_cast<void*>(0)); /* Fill preview */ GtkWidget *w = static_cast<GtkWidget *>(g_object_get_data(G_OBJECT(widget), "preview")); @@ -1227,6 +1249,12 @@ static void sp_gradient_vector_widget_destroy(GtkObject *object, gpointer /*data sp_repr_remove_listener_by_data(gradient->getRepr(), object); } } + + SelectedColor *selected_color = static_cast<SelectedColor *>(g_object_get_data(G_OBJECT(object), "cselector")); + if (selected_color) { + delete selected_color; + g_object_set_data(G_OBJECT(object), "cselector", NULL); + } } static void sp_gradient_vector_gradient_release(SPObject */*object*/, GtkWidget *widget) @@ -1244,7 +1272,7 @@ static void sp_gradient_vector_gradient_modified(SPObject *object, guint /*flags } } -static void sp_gradient_vector_color_dragged(SPColorSelector *csel, GObject *object) +static void sp_gradient_vector_color_dragged(Inkscape::UI::SelectedColor *selected_color, GObject *object) { SPGradient *gradient, *ngr; @@ -1272,14 +1300,21 @@ static void sp_gradient_vector_color_dragged(SPColorSelector *csel, GObject *obj return; } - csel->base->getColorAlpha(stop->specified_color, stop->opacity); + selected_color->colorAlpha(stop->specified_color, stop->opacity); stop->currentColor = false; blocked = FALSE; } -static void sp_gradient_vector_color_changed(SPColorSelector *csel, GObject *object) +static void sp_gradient_vector_color_changed(Inkscape::UI::SelectedColor *selected_color, GObject *object) { + (void)selected_color; + + void* updating_color = g_object_get_data(G_OBJECT(object), "updating_color"); + if (updating_color) { + return; + } + if (blocked) { return; } @@ -1308,10 +1343,10 @@ static void sp_gradient_vector_color_changed(SPColorSelector *csel, GObject *obj return; } - csel = static_cast<SPColorSelector*>(g_object_get_data(G_OBJECT(object), "cselector")); + SelectedColor *csel = static_cast<SelectedColor *>(g_object_get_data(G_OBJECT(object), "cselector")); SPColor color; float alpha = 0; - csel->base->getColorAlpha( color, alpha ); + csel->colorAlpha(color, alpha); sp_repr_set_css_double(stop->getRepr(), "offset", stop->offset); Inkscape::CSSOStringStream os; diff --git a/src/widgets/icon.cpp b/src/widgets/icon.cpp index afc4fcfb0..542d16797 100644 --- a/src/widgets/icon.cpp +++ b/src/widgets/icon.cpp @@ -16,10 +16,6 @@ # include "config.h" #endif -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include <gtkmm/icontheme.h> #include <cstring> #include <glib.h> diff --git a/src/widgets/mesh-toolbar.cpp b/src/widgets/mesh-toolbar.cpp index bf406e632..1af55d9cd 100644 --- a/src/widgets/mesh-toolbar.cpp +++ b/src/widgets/mesh-toolbar.cpp @@ -87,8 +87,9 @@ void ms_read_selection( Inkscape::Selection *selection, bool first = true; ms_type = SP_MESH_TYPE_COONS; - for (GSList const* i = selection->itemList(); i; i = i->next) { - SPItem *item = SP_ITEM(i->data); + std::vector<SPItem*> itemlist=selection->itemList(); + for(std::vector<SPItem*>::const_iterator i=itemlist.begin();i!=itemlist.end();i++){ + SPItem *item = *i; SPStyle *style = item->style; if (style && (style->fill.isPaintserver())) { @@ -213,8 +214,9 @@ void ms_get_dt_selected_gradient(Inkscape::Selection *selection, SPMesh *&ms_sel { SPMesh *gradient = 0; - for (GSList const* i = selection->itemList(); i; i = i->next) { - SPItem *item = SP_ITEM(i->data); // get the items gradient, not the getVector() version + std::vector<SPItem*> itemlist=selection->itemList(); + for(std::vector<SPItem*>::const_iterator i=itemlist.begin();i!=itemlist.end();i++){ + SPItem *item = *i;// get the items gradient, not the getVector() version SPStyle *style = item->style; SPPaintServer *server = 0; diff --git a/src/widgets/paint-selector.cpp b/src/widgets/paint-selector.cpp index 948c80db3..846ded511 100644 --- a/src/widgets/paint-selector.cpp +++ b/src/widgets/paint-selector.cpp @@ -24,10 +24,6 @@ #include <cstring> #include <string> -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include "widgets/swatch-selector.h" #include "sp-pattern.h" #include <glibmm/i18n.h> @@ -35,10 +31,10 @@ #include "widgets/widget-sizes.h" #include "xml/repr.h" -#include "sp-color-notebook.h" #include "sp-linear-gradient.h" #include "sp-radial-gradient.h" #include "sp-mesh.h" +#include "sp-stop.h" /* fixme: Move it from dialogs to here */ #include "gradient-selector.h" #include <inkscape.h> @@ -51,6 +47,7 @@ #include "io/sys.h" #include "helper/stock-items.h" #include "ui/icon-names.h" +#include "ui/widget/color-notebook.h" #include "paint-selector.h" @@ -61,6 +58,7 @@ #include <gtk/gtk.h> using Inkscape::Widgets::SwatchSelector; +using Inkscape::UI::SelectedColor; enum { MODE_CHANGED, @@ -97,8 +95,7 @@ static gchar const* modeStrings[] = { "MODE_EMPTY", "MODE_MULTIPLE", "MODE_NONE", - "MODE_COLOR_RGB", - "MODE_COLOR_CMYK", + "MODE_SOLID_COLOR", "MODE_GRADIENT_LINEAR", "MODE_GRADIENT_RADIAL", "MODE_PATTERN", @@ -221,7 +218,7 @@ sp_paint_selector_init(SPPaintSelector *psel) psel->none = sp_paint_selector_style_button_add(psel, INKSCAPE_ICON("paint-none"), SPPaintSelector::MODE_NONE, _("No paint")); psel->solid = sp_paint_selector_style_button_add(psel, INKSCAPE_ICON("paint-solid"), - SPPaintSelector::MODE_COLOR_RGB, _("Flat color")); + SPPaintSelector::MODE_SOLID_COLOR, _("Flat color")); psel->gradient = sp_paint_selector_style_button_add(psel, INKSCAPE_ICON("paint-gradient-linear"), SPPaintSelector::MODE_GRADIENT_LINEAR, _("Linear gradient")); psel->radial = sp_paint_selector_style_button_add(psel, INKSCAPE_ICON("paint-gradient-radial"), @@ -295,8 +292,13 @@ sp_paint_selector_init(SPPaintSelector *psel) /* Last used color */ - psel->color.set( 0.0, 0.0, 0.0 ); - psel->alpha = 1.0; + psel->selected_color = new SelectedColor; + psel->updating_color = false; + + psel->selected_color->signal_grabbed.connect(sigc::mem_fun(psel, &SPPaintSelector::onSelectedColorGrabbed)); + psel->selected_color->signal_dragged.connect(sigc::mem_fun(psel, &SPPaintSelector::onSelectedColorDragged)); + psel->selected_color->signal_released.connect(sigc::mem_fun(psel, &SPPaintSelector::onSelectedColorReleased)); + psel->selected_color->signal_changed.connect(sigc::mem_fun(psel, &SPPaintSelector::onSelectedColorChanged)); } static void sp_paint_selector_dispose(GObject *object) @@ -306,6 +308,11 @@ static void sp_paint_selector_dispose(GObject *object) // clean up our long-living pattern menu g_object_set_data(G_OBJECT(psel),"patternmenu",NULL); + if (psel->selected_color) { + delete psel->selected_color; + psel->selected_color = NULL; + } + if ((G_OBJECT_CLASS(sp_paint_selector_parent_class))->dispose) (G_OBJECT_CLASS(sp_paint_selector_parent_class))->dispose(object); } @@ -399,8 +406,7 @@ void SPPaintSelector::setMode(Mode mode) case MODE_NONE: sp_paint_selector_set_mode_none(this); break; - case MODE_COLOR_RGB: - case MODE_COLOR_CMYK: + case MODE_SOLID_COLOR: sp_paint_selector_set_mode_color(this, mode); break; case MODE_GRADIENT_LINEAR: @@ -441,7 +447,6 @@ void SPPaintSelector::setFillrule(FillRule fillrule) void SPPaintSelector::setColorAlpha(SPColor const &color, float alpha) { g_return_if_fail( ( 0.0 <= alpha ) && ( alpha <= 1.0 ) ); - SPColorSelector *csel = 0; /* guint32 rgba = 0; @@ -458,12 +463,13 @@ void SPPaintSelector::setColorAlpha(SPColor const &color, float alpha) #ifdef SP_PS_VERBOSE g_print("PaintSelector set RGBA\n"); #endif - setMode(MODE_COLOR_RGB); + setMode(MODE_SOLID_COLOR); } - csel = reinterpret_cast<SPColorSelector*>(g_object_get_data(G_OBJECT(selector), "color-selector")); + updating_color = true; + selected_color->setColorAlpha(color, alpha); + updating_color = false; //rgba = color.toRGBA32( alpha ); - csel->base->setColorAlpha( color, alpha ); } void SPPaintSelector::setSwatch(SPGradient *vector ) @@ -544,11 +550,7 @@ void SPPaintSelector::getGradientProperties( SPGradientUnits &units, SPGradientS */ void SPPaintSelector::getColorAlpha(SPColor &color, gfloat &alpha) const { - SPColorSelector *csel; - - csel = reinterpret_cast<SPColorSelector*>(g_object_get_data(G_OBJECT(selector), "color-selector")); - - csel->base->getColorAlpha( color, alpha ); + selected_color->colorAlpha(color, alpha); g_assert( ( 0.0 <= alpha ) && ( alpha <= 1.0 ) ); @@ -584,6 +586,9 @@ sp_paint_selector_clear_frame(SPPaintSelector *psel) if (psel->selector) { + //This is a hack to work around GtkNotebook bug in ColorSelector. Is sends signal switch-page on destroy + //The widget is hidden firts so it can recognize that it should not process signals from notebook child + gtk_widget_set_visible(psel->selector, false); gtk_widget_destroy(psel->selector); psel->selector = NULL; } @@ -636,69 +641,82 @@ sp_paint_selector_set_mode_none(SPPaintSelector *psel) /* Color paint */ -static void sp_paint_selector_color_grabbed(SPColorSelector * /*csel*/, SPPaintSelector *psel) -{ - g_signal_emit(G_OBJECT(psel), psel_signals[GRABBED], 0); +void SPPaintSelector::onSelectedColorGrabbed() { + g_signal_emit(G_OBJECT(this), psel_signals[GRABBED], 0); } -static void sp_paint_selector_color_dragged(SPColorSelector * /*csel*/, SPPaintSelector *psel) -{ - g_signal_emit(G_OBJECT(psel), psel_signals[DRAGGED], 0); +void SPPaintSelector::onSelectedColorDragged() { + if (updating_color) { + return; + } + g_signal_emit(G_OBJECT(this), psel_signals[DRAGGED], 0); } -static void sp_paint_selector_color_released(SPColorSelector * /*csel*/, SPPaintSelector *psel) -{ - g_signal_emit(G_OBJECT(psel), psel_signals[RELEASED], 0); +void SPPaintSelector::onSelectedColorReleased() { + g_signal_emit(G_OBJECT(this), psel_signals[RELEASED], 0); } -static void -sp_paint_selector_color_changed(SPColorSelector *csel, SPPaintSelector *psel) -{ - csel->base->getColorAlpha( psel->color, psel->alpha ); +void SPPaintSelector::onSelectedColorChanged() { + if (updating_color) { + return; + } - g_signal_emit(G_OBJECT(psel), psel_signals[CHANGED], 0); + if (mode == MODE_SOLID_COLOR) { + g_signal_emit(G_OBJECT(this), psel_signals[CHANGED], 0); + } else { + g_warning("SPPaintSelector::onSelectedColorChanged(): selected color changed while not in color selection mode"); + } } static void sp_paint_selector_set_mode_color(SPPaintSelector *psel, SPPaintSelector::Mode /*mode*/) { - GtkWidget *csel; + using Inkscape::UI::Widget::ColorNotebook; + + if ((psel->mode == SPPaintSelector::MODE_SWATCH) + || (psel->mode == SPPaintSelector::MODE_GRADIENT_LINEAR) + || (psel->mode == SPPaintSelector::MODE_GRADIENT_RADIAL) ) { + SPGradientSelector *gsel = getGradientFromData(psel); + if (gsel) { + SPGradient *gradient = gsel->getVector(); + + // Gradient can be null if object paint is changed externally (ie. with a color picker tool) + if (gradient) + { + SPColor color = gradient->getFirstStop()->specified_color; + float alpha = gradient->getFirstStop()->opacity; + psel->selected_color->setColorAlpha(color, alpha, false); + } + } + } sp_paint_selector_set_style_buttons(psel, psel->solid); gtk_widget_set_sensitive(psel->style, TRUE); - if ((psel->mode == SPPaintSelector::MODE_COLOR_RGB) || (psel->mode == SPPaintSelector::MODE_COLOR_CMYK)) { + if ((psel->mode == SPPaintSelector::MODE_SOLID_COLOR)) { /* Already have color selector */ - csel = GTK_WIDGET(g_object_get_data(G_OBJECT(psel->selector), "color-selector")); + // Do nothing } else { sp_paint_selector_clear_frame(psel); /* Create new color selector */ /* Create vbox */ #if GTK_CHECK_VERSION(3,0,0) - GtkWidget *vb = gtk_box_new(GTK_ORIENTATION_VERTICAL, 4); - gtk_box_set_homogeneous(GTK_BOX(vb), FALSE); + GtkWidget *vb = gtk_box_new(GTK_ORIENTATION_VERTICAL, 4); + gtk_box_set_homogeneous(GTK_BOX(vb), FALSE); #else GtkWidget *vb = gtk_vbox_new(FALSE, 4); #endif gtk_widget_show(vb); /* Color selector */ - csel = sp_color_selector_new( SP_TYPE_COLOR_NOTEBOOK ); - gtk_widget_show(csel); - g_object_set_data(G_OBJECT(vb), "color-selector", csel); - gtk_box_pack_start(GTK_BOX(vb), csel, TRUE, TRUE, 0); - g_signal_connect(G_OBJECT(csel), "grabbed", G_CALLBACK(sp_paint_selector_color_grabbed), psel); - g_signal_connect(G_OBJECT(csel), "dragged", G_CALLBACK(sp_paint_selector_color_dragged), psel); - g_signal_connect(G_OBJECT(csel), "released", G_CALLBACK(sp_paint_selector_color_released), psel); - g_signal_connect(G_OBJECT(csel), "changed", G_CALLBACK(sp_paint_selector_color_changed), psel); + Gtk::Widget *color_selector = Gtk::manage(new ColorNotebook(*(psel->selected_color))); + color_selector->show(); + gtk_box_pack_start(GTK_BOX(vb), color_selector->gobj(), TRUE, TRUE, 0); + /* Pack everything to frame */ gtk_container_add(GTK_CONTAINER(psel->frame), vb); psel->selector = vb; - - /* Set color */ - SP_COLOR_SELECTOR( csel )->base->setColorAlpha( psel->color, psel->alpha ); - } gtk_label_set_markup(GTK_LABEL(psel->label), _("<b>Flat color</b>")); @@ -710,22 +728,22 @@ static void sp_paint_selector_set_mode_color(SPPaintSelector *psel, SPPaintSelec /* Gradient */ -static void sp_paint_selector_gradient_grabbed(SPColorSelector * /*csel*/, SPPaintSelector *psel) +static void sp_paint_selector_gradient_grabbed(SPGradientSelector * /*csel*/, SPPaintSelector *psel) { g_signal_emit(G_OBJECT(psel), psel_signals[GRABBED], 0); } -static void sp_paint_selector_gradient_dragged(SPColorSelector * /*csel*/, SPPaintSelector *psel) +static void sp_paint_selector_gradient_dragged(SPGradientSelector * /*csel*/, SPPaintSelector *psel) { g_signal_emit(G_OBJECT(psel), psel_signals[DRAGGED], 0); } -static void sp_paint_selector_gradient_released(SPColorSelector * /*csel*/, SPPaintSelector *psel) +static void sp_paint_selector_gradient_released(SPGradientSelector * /*csel*/, SPPaintSelector *psel) { g_signal_emit(G_OBJECT(psel), psel_signals[RELEASED], 0); } -static void sp_paint_selector_gradient_changed(SPColorSelector * /*csel*/, SPPaintSelector *psel) +static void sp_paint_selector_gradient_changed(SPGradientSelector * /*csel*/, SPPaintSelector *psel) { g_signal_emit(G_OBJECT(psel), psel_signals[CHANGED], 0); } @@ -828,7 +846,7 @@ ink_pattern_list_get (SPDocument *source) GSList *pl = NULL; GSList const *patterns = source->getResourceList("pattern"); for (GSList *l = const_cast<GSList *>(patterns); l != NULL; l = l->next) { - if (SP_PATTERN(l->data) == pattern_getroot(SP_PATTERN(l->data))) { // only if this is a root pattern + if (SP_PATTERN(l->data) == SP_PATTERN(l->data)->rootPattern()) { // only if this is a root pattern pl = g_slist_prepend(pl, l->data); } } @@ -1148,7 +1166,7 @@ SPPattern *SPPaintSelector::getPattern() } g_free(paturn); } else { - pat = pattern_getroot(SP_PATTERN(patid)); + pat = SP_PATTERN(patid)->rootPattern(); } if (pat && !SP_IS_PATTERN(pat)) { @@ -1255,7 +1273,7 @@ SPPaintSelector::Mode SPPaintSelector::getModeForStyle(SPStyle const & style, Fi } } else if ( target.isColor() ) { // TODO this is no longer a valid assertion: - mode = MODE_COLOR_RGB; // so far only rgb can be read from svg + mode = MODE_SOLID_COLOR; // so far only rgb can be read from svg } else if ( target.isNone() ) { mode = MODE_NONE; } else { diff --git a/src/widgets/paint-selector.h b/src/widgets/paint-selector.h index 788aa673e..23c2dd456 100644 --- a/src/widgets/paint-selector.h +++ b/src/widgets/paint-selector.h @@ -12,12 +12,18 @@ * */ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <glib.h> #include <gtk/gtk.h> #include "color.h" #include "fill-or-stroke.h" #include "sp-gradient-spread.h" #include "sp-gradient-units.h" +#include "ui/selected-color.h" class SPGradient; class SPDesktop; @@ -44,8 +50,7 @@ struct SPPaintSelector { MODE_EMPTY, MODE_MULTIPLE, MODE_NONE, - MODE_COLOR_RGB, - MODE_COLOR_CMYK, + MODE_SOLID_COLOR, MODE_GRADIENT_LINEAR, MODE_GRADIENT_RADIAL, #ifdef WITH_MESH @@ -83,9 +88,8 @@ struct SPPaintSelector { GtkWidget *frame, *selector; GtkWidget *label; - SPColor color; - float alpha; - + Inkscape::UI::SelectedColor *selected_color; + bool updating_color; static Mode getModeForStyle(SPStyle const & style, FillOrStroke kind); @@ -114,6 +118,11 @@ struct SPPaintSelector { // TODO move this elsewhere: void setFlatColor( SPDesktop *desktop, const gchar *color_property, const gchar *opacity_property ); + + void onSelectedColorGrabbed(); + void onSelectedColorDragged(); + void onSelectedColorReleased(); + void onSelectedColorChanged(); }; enum {COMBO_COL_LABEL=0, COMBO_COL_STOCK=1, COMBO_COL_PATTERN=2, COMBO_COL_SEP=3, COMBO_N_COLS=4}; diff --git a/src/widgets/paintbucket-toolbar.cpp b/src/widgets/paintbucket-toolbar.cpp index d8edeb9f6..eb55287c4 100644 --- a/src/widgets/paintbucket-toolbar.cpp +++ b/src/widgets/paintbucket-toolbar.cpp @@ -121,17 +121,16 @@ void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions { GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT ); - GList* items = 0; gint count = 0; - for ( items = Inkscape::UI::Tools::flood_channels_dropdown_items_list(); items ; items = g_list_next(items) ) - { + const std::vector<Glib::ustring>& channel_list = Inkscape::UI::Tools::FloodTool::channel_list; + for (std::vector<Glib::ustring>::const_iterator iterator = channel_list.begin(); + iterator != channel_list.end(); ++iterator ) { GtkTreeIter iter; gtk_list_store_append( model, &iter ); - gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 ); + gtk_list_store_set( model, &iter, 0, (*iterator).c_str(), 1, count, -1 ); count++; } - g_list_free( items ); - items = 0; + EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) ); g_object_set( act1, "short_label", _("Fill by:"), NULL ); ege_select_one_action_set_appearance( act1, "compact" ); @@ -188,17 +187,15 @@ void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions { GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT ); - GList* items = 0; gint count = 0; - for ( items = Inkscape::UI::Tools::flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) ) - { + const std::vector<Glib::ustring>& gap_list = Inkscape::UI::Tools::FloodTool::gap_list; + for (std::vector<Glib::ustring>::const_iterator iterator = gap_list.begin(); + iterator != gap_list.end(); ++iterator ) { GtkTreeIter iter; gtk_list_store_append( model, &iter ); - gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 ); + gtk_list_store_set( model, &iter, 0, (*iterator).c_str(), 1, count, -1 ); count++; } - g_list_free( items ); - items = 0; EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) ); g_object_set( act2, "short_label", _("Close gaps:"), NULL ); ege_select_one_action_set_appearance( act2, "compact" ); diff --git a/src/widgets/rect-toolbar.cpp b/src/widgets/rect-toolbar.cpp index 46f6d1c23..96ba699dc 100644 --- a/src/widgets/rect-toolbar.cpp +++ b/src/widgets/rect-toolbar.cpp @@ -106,12 +106,13 @@ static void sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const * bool modmade = false; Inkscape::Selection *selection = desktop->getSelection(); - for (GSList const *items = selection->itemList(); items != NULL; items = items->next) { - if (SP_IS_RECT(items->data)) { + std::vector<SPItem*> itemlist=selection->itemList(); + for(std::vector<SPItem*>::const_iterator i=itemlist.begin();i!=itemlist.end();i++){ + if (SP_IS_RECT(*i)) { if (gtk_adjustment_get_value(adj) != 0) { - (SP_RECT(items->data)->*setter)(Quantity::convert(gtk_adjustment_get_value(adj), unit, "px")); + (SP_RECT(*i)->*setter)(Quantity::convert(gtk_adjustment_get_value(adj), unit, "px")); } else { - SP_OBJECT(items->data)->getRepr()->setAttribute(value_name, NULL); + (*i)->getRepr()->setAttribute(value_name, NULL); } modmade = true; } @@ -242,12 +243,11 @@ static void sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GO } purge_repr_listener( tbl, tbl ); - for (GSList const *items = selection->itemList(); - items != NULL; - items = items->next) { - if (SP_IS_RECT(reinterpret_cast<SPItem *>(items->data))) { + std::vector<SPItem*> itemlist=selection->itemList(); + for(std::vector<SPItem*>::const_iterator i=itemlist.begin();i!=itemlist.end();i++){ + if (SP_IS_RECT(*i)) { n_selected++; - item = reinterpret_cast<SPItem *>(items->data); + item = *i; repr = item->getRepr(); } } diff --git a/src/widgets/shrink-wrap-button.cpp b/src/widgets/shrink-wrap-button.cpp index 3558780ed..941a0466c 100644 --- a/src/widgets/shrink-wrap-button.cpp +++ b/src/widgets/shrink-wrap-button.cpp @@ -13,10 +13,6 @@ #include "config.h" #endif -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include <gtkmm/button.h> #include <gtk/gtk.h> diff --git a/src/widgets/sp-attribute-widget.h b/src/widgets/sp-attribute-widget.h index 4bd6c6959..d9b972201 100644 --- a/src/widgets/sp-attribute-widget.h +++ b/src/widgets/sp-attribute-widget.h @@ -19,10 +19,6 @@ # include "config.h" #endif -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include <gtkmm/widget.h> #include <stddef.h> #include <sigc++/connection.h> diff --git a/src/widgets/sp-color-icc-selector.cpp b/src/widgets/sp-color-icc-selector.cpp deleted file mode 100644 index 6e910c582..000000000 --- a/src/widgets/sp-color-icc-selector.cpp +++ /dev/null @@ -1,1113 +0,0 @@ -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include "gradient-vector.h" -#include <math.h> -#include <gtk/gtk.h> -#include <glibmm/i18n.h> -#include <map> -#include <set> -#include <vector> - -#include "ui/dialog-events.h" -#include "sp-color-icc-selector.h" -#include "sp-color-scales.h" -#include "sp-color-slider.h" -#include "svg/svg-icc-color.h" -#include "colorspace.h" -#include "document.h" -#include "inkscape.h" -#include "profile-manager.h" - -#define noDEBUG_LCMS - -#if defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) -#include "color-profile.h" -#include "cms-system.h" -#include "color-profile-cms-fns.h" - -#ifdef DEBUG_LCMS -#include "preferences.h" -#endif // DEBUG_LCMS -#endif // defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) - - -#ifdef DEBUG_LCMS -extern guint update_in_progress; -#define DEBUG_MESSAGE(key, ...) \ -{\ - Inkscape::Preferences *prefs = Inkscape::Preferences::get();\ - bool dump = prefs->getBool("/options/scislac/" #key);\ - bool dumpD = prefs->getBool("/options/scislac/" #key "D");\ - bool dumpD2 = prefs->getBool("/options/scislac/" #key "D2");\ - dumpD &&= ( (update_in_progress == 0) || dumpD2 );\ - if ( dump )\ - {\ - g_message( __VA_ARGS__ );\ -\ - }\ - if ( dumpD )\ - {\ - GtkWidget *dialog = gtk_message_dialog_new(NULL,\ - GTK_DIALOG_DESTROY_WITH_PARENT, \ - GTK_MESSAGE_INFO, \ - GTK_BUTTONS_OK, \ - __VA_ARGS__ \ - );\ - g_signal_connect_swapped(dialog, "response",\ - G_CALLBACK(gtk_widget_destroy), \ - dialog); \ - gtk_widget_show_all( dialog );\ - }\ -} -#endif // DEBUG_LCMS - - - -G_BEGIN_DECLS - -static void sp_color_icc_selector_dispose(GObject *object); -static void sp_color_icc_selector_show_all (GtkWidget *widget); -static void sp_color_icc_selector_hide(GtkWidget *widget); - -G_END_DECLS - -/** - * Class containing the parts for a single color component's UI presence. - */ -class ComponentUI -{ -public: - ComponentUI() : - _component(), - _adj(0), - _slider(0), - _btn(0), - _label(0), - _map(0) - { - } - - ComponentUI(colorspace::Component const &component) : - _component(component), - _adj(0), - _slider(0), - _btn(0), - _label(0), - _map(0) - { - } - - colorspace::Component _component; - GtkAdjustment *_adj; // Component adjustment - GtkWidget *_slider; - GtkWidget *_btn; // spinbutton - GtkWidget *_label; // Label - guchar *_map; -}; - -/** - * Class that implements the internals of the selector. - */ -class ColorICCSelectorImpl -{ -public: - - ColorICCSelectorImpl( ColorICCSelector *owner); - - ~ColorICCSelectorImpl(); - - static void _adjustmentChanged ( GtkAdjustment *adjustment, SPColorICCSelector *cs ); - - static void _sliderGrabbed( SPColorSlider *slider, SPColorICCSelector *cs ); - static void _sliderReleased( SPColorSlider *slider, SPColorICCSelector *cs ); - static void _sliderChanged( SPColorSlider *slider, SPColorICCSelector *cs ); - - static void _profileSelected( GtkWidget* src, gpointer data ); - static void _fixupHit( GtkWidget* src, gpointer data ); - -#if defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) - void _setProfile( SVGICCColor* profile ); - void _switchToProfile( gchar const* name ); -#endif - void _updateSliders( gint ignore ); - void _profilesChanged( std::string const & name ); - - ColorICCSelector *_owner; - - gboolean _updating : 1; - gboolean _dragging : 1; - - guint32 _fixupNeeded; - GtkWidget* _fixupBtn; - GtkWidget* _profileSel; - - std::vector<ComponentUI> _compUI; - - GtkAdjustment* _adj; // Channel adjustment - GtkWidget* _slider; - GtkWidget* _sbtn; // Spinbutton - GtkWidget* _label; // Label - -#if defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) - std::string _profileName; - Inkscape::ColorProfile* _prof; - guint _profChannelCount; - gulong _profChangedID; -#endif // defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) -}; - -#define XPAD 4 -#define YPAD 1 - -namespace -{ - -size_t maxColorspaceComponentCount = 0; - -#if defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) - -/** - * Internal variable to track all known colorspaces. - */ -std::set<cmsUInt32Number> knownColorspaces; - -#endif - - -/** - * Simple helper to allow bitwise or on GtkAttachOptions. - */ -GtkAttachOptions operator|(GtkAttachOptions lhs, GtkAttachOptions rhs) -{ - return static_cast<GtkAttachOptions>(static_cast<int>(lhs) | static_cast<int>(rhs)); -} - -/** - * Helper function to handle GTK2/GTK3 attachment #ifdef code. - */ -void attachToGridOrTable(GtkWidget *parent, - GtkWidget *child, - guint left, - guint top, - guint width, - guint height, - bool hexpand = false, - bool centered = false, - guint xpadding = XPAD, - guint ypadding = YPAD) -{ -#if GTK_CHECK_VERSION(3,0,0) - #if GTK_CHECK_VERSION(3,12,0) - gtk_widget_set_margin_start( child, xpadding ); - gtk_widget_set_margin_end( child, xpadding ); - #else - gtk_widget_set_margin_left( child, xpadding ); - gtk_widget_set_margin_right( child, xpadding ); - #endif - - gtk_widget_set_margin_top( child, ypadding ); - gtk_widget_set_margin_bottom( child, ypadding ); - if (hexpand) { - gtk_widget_set_hexpand(child, TRUE); - } - if (centered) { - gtk_widget_set_halign( child, GTK_ALIGN_CENTER ); - gtk_widget_set_valign( child, GTK_ALIGN_CENTER ); - } - gtk_grid_attach( GTK_GRID(parent), child, left, top, width, height ); -#else - GtkAttachOptions xoptions = centered ? static_cast<GtkAttachOptions>(0) : hexpand ? (GTK_EXPAND | GTK_FILL) : GTK_FILL; - GtkAttachOptions yoptions = centered ? static_cast<GtkAttachOptions>(0) : GTK_FILL; - - gtk_table_attach( GTK_TABLE(parent), child, left, left + width, top, top + height, xoptions, yoptions, xpadding, ypadding ); -#endif -} - -} // namespace - -G_DEFINE_TYPE(SPColorICCSelector, sp_color_icc_selector, SP_TYPE_COLOR_SELECTOR); - -static void sp_color_icc_selector_class_init(SPColorICCSelectorClass *klass) -{ - static const gchar* nameset[] = {N_("CMS"), 0}; - GObjectClass *object_class = G_OBJECT_CLASS(klass); - GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass); - SPColorSelectorClass *selector_class = SP_COLOR_SELECTOR_CLASS (klass); - - selector_class->name = nameset; - selector_class->submode_count = 1; - - object_class->dispose = sp_color_icc_selector_dispose; - - widget_class->show_all = sp_color_icc_selector_show_all; - widget_class->hide = sp_color_icc_selector_hide; -} - - -ColorICCSelector::ColorICCSelector( SPColorSelector* csel ) - : ColorSelector( csel ), - _impl(NULL) -{ -} - -ColorICCSelector::~ColorICCSelector() -{ - if (_impl) - { - delete _impl; - _impl = 0; - } -} - -void sp_color_icc_selector_init(SPColorICCSelector *cs) -{ - SP_COLOR_SELECTOR(cs)->base = new ColorICCSelector( SP_COLOR_SELECTOR(cs) ); - - if ( SP_COLOR_SELECTOR(cs)->base ) - { - SP_COLOR_SELECTOR(cs)->base->init(); - } -} - - -/* -icSigRgbData -icSigCmykData -icSigCmyData -*/ -#define SPACE_ID_RGB 0 -#define SPACE_ID_CMY 1 -#define SPACE_ID_CMYK 2 - - -#if defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) -static cmsUInt16Number* getScratch() { - // bytes per pixel * input channels * width - static cmsUInt16Number* scritch = static_cast<cmsUInt16Number*>(g_new(cmsUInt16Number, 4 * 1024)); - - return scritch; -} - -colorspace::Component::Component() : - name(), - tip(), - scale(1) -{ -} - -colorspace::Component::Component(std::string const &name, std::string const &tip, guint scale) : - name(name), - tip(tip), - scale(scale) -{ -} - -std::vector<colorspace::Component> colorspace::getColorSpaceInfo( uint32_t space ) -{ - static std::map<cmsUInt32Number, std::vector<Component> > sets; - if (sets.empty()) - { - sets[cmsSigXYZData].push_back(Component("_X", "X", 2)); // TYPE_XYZ_16 - sets[cmsSigXYZData].push_back(Component("_Y", "Y", 1)); - sets[cmsSigXYZData].push_back(Component("_Z", "Z", 2)); - - sets[cmsSigLabData].push_back(Component("_L", "L", 100)); // TYPE_Lab_16 - sets[cmsSigLabData].push_back(Component("_a", "a", 256)); - sets[cmsSigLabData].push_back(Component("_b", "b", 256)); - - //cmsSigLuvData - - sets[cmsSigYCbCrData].push_back(Component("_Y", "Y", 1)); // TYPE_YCbCr_16 - sets[cmsSigYCbCrData].push_back(Component("C_b", "Cb", 1)); - sets[cmsSigYCbCrData].push_back(Component("C_r", "Cr", 1)); - - sets[cmsSigYxyData].push_back(Component("_Y", "Y", 1)); // TYPE_Yxy_16 - sets[cmsSigYxyData].push_back(Component("_x", "x", 1)); - sets[cmsSigYxyData].push_back(Component("y", "y", 1)); - - sets[cmsSigRgbData].push_back(Component(_("_R:"), _("Red"), 1)); // TYPE_RGB_16 - sets[cmsSigRgbData].push_back(Component(_("_G:"), _("Green"), 1)); - sets[cmsSigRgbData].push_back(Component(_("_B:"), _("Blue"), 1)); - - sets[cmsSigGrayData].push_back(Component(_("G:"), _("Gray"), 1)); // TYPE_GRAY_16 - - sets[cmsSigHsvData].push_back(Component(_("_H:"), _("Hue"), 360)); // TYPE_HSV_16 - sets[cmsSigHsvData].push_back(Component(_("_S:"), _("Saturation"), 1)); - sets[cmsSigHsvData].push_back(Component("_V:", "Value", 1)); - - sets[cmsSigHlsData].push_back(Component(_("_H:"), _("Hue"), 360)); // TYPE_HLS_16 - sets[cmsSigHlsData].push_back(Component(_("_L:"), _("Lightness"), 1)); - sets[cmsSigHlsData].push_back(Component(_("_S:"), _("Saturation"), 1)); - - sets[cmsSigCmykData].push_back(Component(_("_C:"), _("Cyan"), 1)); // TYPE_CMYK_16 - sets[cmsSigCmykData].push_back(Component(_("_M:"), _("Magenta"), 1)); - sets[cmsSigCmykData].push_back(Component(_("_Y:"), _("Yellow"), 1)); - sets[cmsSigCmykData].push_back(Component(_("_K:"), _("Black"), 1)); - - sets[cmsSigCmyData].push_back(Component(_("_C:"), _("Cyan"), 1)); // TYPE_CMY_16 - sets[cmsSigCmyData].push_back(Component(_("_M:"), _("Magenta"), 1)); - sets[cmsSigCmyData].push_back(Component(_("_Y:"), _("Yellow"), 1)); - - for (std::map<cmsUInt32Number, std::vector<Component> >::iterator it = sets.begin(); it != sets.end(); ++it) - { - knownColorspaces.insert(it->first); - maxColorspaceComponentCount = std::max(maxColorspaceComponentCount, it->second.size()); - } - } - - std::vector<Component> target; - - if (sets.find(space) != sets.end()) - { - target = sets[space]; - } - return target; -} - - -std::vector<colorspace::Component> colorspace::getColorSpaceInfo( Inkscape::ColorProfile *prof ) -{ - return getColorSpaceInfo( asICColorSpaceSig(prof->getColorSpace()) ); -} - -#endif // defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) - -ColorICCSelectorImpl::ColorICCSelectorImpl(ColorICCSelector *owner) : - _owner(owner), - _updating( FALSE ), - _dragging( FALSE ), - _fixupNeeded(0), - _fixupBtn(0), - _profileSel(0), - _compUI(), - _adj(0), - _slider(0), - _sbtn(0), - _label(0) -#if defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) - , - _profileName(), - _prof(0), - _profChannelCount(0), - _profChangedID(0) -#endif // defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) -{ -} - -ColorICCSelectorImpl::~ColorICCSelectorImpl() -{ - _adj = 0; - _sbtn = 0; - _label = 0; -} - -void ColorICCSelector::init() -{ - if (_impl) delete(_impl); - _impl = new ColorICCSelectorImpl(this); - gint row = 0; - - _impl->_updating = FALSE; - _impl->_dragging = FALSE; - -#if GTK_CHECK_VERSION(3,0,0) - GtkWidget *t = gtk_grid_new(); -#else - GtkWidget *t = gtk_table_new(5, 3, FALSE); -#endif - - gtk_widget_show (t); - gtk_box_pack_start (GTK_BOX (_csel), t, TRUE, TRUE, 4); - - _impl->_compUI.clear(); - - // Create components - row = 0; - - - _impl->_fixupBtn = gtk_button_new_with_label(_("Fix")); - g_signal_connect( G_OBJECT(_impl->_fixupBtn), "clicked", G_CALLBACK(ColorICCSelectorImpl::_fixupHit), (gpointer)_impl ); - gtk_widget_set_sensitive( _impl->_fixupBtn, FALSE ); - gtk_widget_set_tooltip_text( _impl->_fixupBtn, _("Fix RGB fallback to match icc-color() value.") ); - //gtk_misc_set_alignment( GTK_MISC (_impl->_fixupBtn), 1.0, 0.5 ); - gtk_widget_show( _impl->_fixupBtn ); - - attachToGridOrTable(t, _impl->_fixupBtn, 0, row, 1, 1); - - // Combobox and store with 2 columns : label (0) and full name (1) - GtkListStore *store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING); - _impl->_profileSel = gtk_combo_box_new_with_model (GTK_TREE_MODEL (store)); - - GtkCellRenderer *renderer = gtk_cell_renderer_text_new (); - gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(_impl->_profileSel), renderer, TRUE); - gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(_impl->_profileSel), renderer, "text", 0, NULL); - - GtkTreeIter iter; - gtk_list_store_append (store, &iter); - gtk_list_store_set (store, &iter, 0, _("<none>"), 1, _("<none>"), -1); - - gtk_widget_show( _impl->_profileSel ); - gtk_combo_box_set_active( GTK_COMBO_BOX(_impl->_profileSel), 0 ); - - attachToGridOrTable(t, _impl->_profileSel, 1, row, 1, 1); - -#if defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) - _impl->_profChangedID = g_signal_connect( G_OBJECT(_impl->_profileSel), "changed", G_CALLBACK(ColorICCSelectorImpl::_profileSelected), (gpointer)_impl ); -#else - gtk_widget_set_sensitive( _impl->_profileSel, false ); -#endif // defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) - - - row++; - - // populate the data for colorspaces and channels: -#if defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) - std::vector<colorspace::Component> things = colorspace::getColorSpaceInfo( cmsSigRgbData ); -#endif // defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) - - for ( size_t i = 0; i < maxColorspaceComponentCount; i++ ) { -#if defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) - if (i < things.size()) { - _impl->_compUI.push_back(ComponentUI(things[i])); - } else { - _impl->_compUI.push_back(ComponentUI()); - } - - std::string labelStr = (i < things.size()) ? things[i].name.c_str() : ""; -#else - _impl->_compUI.push_back(ComponentUI()); - - std::string labelStr = "."; -#endif - - _impl->_compUI[i]._label = gtk_label_new_with_mnemonic( labelStr.c_str() ); - gtk_misc_set_alignment( GTK_MISC (_impl->_compUI[i]._label), 1.0, 0.5 ); - gtk_widget_show( _impl->_compUI[i]._label ); - - attachToGridOrTable(t, _impl->_compUI[i]._label, 0, row, 1, 1); - - // Adjustment - guint scaleValue = _impl->_compUI[i]._component.scale; - gdouble step = static_cast<gdouble>(scaleValue) / 100.0; - gdouble page = static_cast<gdouble>(scaleValue) / 10.0; - gint digits = (step > 0.9) ? 0 : 2; - _impl->_compUI[i]._adj = GTK_ADJUSTMENT( gtk_adjustment_new( 0.0, 0.0, scaleValue, step, page, page ) ); - - // Slider - _impl->_compUI[i]._slider = sp_color_slider_new( _impl->_compUI[i]._adj ); -#if defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) - gtk_widget_set_tooltip_text( _impl->_compUI[i]._slider, (i < things.size()) ? things[i].tip.c_str() : "" ); -#else - gtk_widget_set_tooltip_text( _impl->_compUI[i]._slider, "." ); -#endif // defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) - gtk_widget_show( _impl->_compUI[i]._slider ); - - attachToGridOrTable(t, _impl->_compUI[i]._slider, 1, row, 1, 1, true); - - _impl->_compUI[i]._btn = gtk_spin_button_new( _impl->_compUI[i]._adj, step, digits ); -#if defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) - gtk_widget_set_tooltip_text( _impl->_compUI[i]._btn, (i < things.size()) ? things[i].tip.c_str() : "" ); -#else - gtk_widget_set_tooltip_text( _impl->_compUI[i]._btn, "." ); -#endif // defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) - sp_dialog_defocus_on_enter( _impl->_compUI[i]._btn ); - gtk_label_set_mnemonic_widget( GTK_LABEL(_impl->_compUI[i]._label), _impl->_compUI[i]._btn ); - gtk_widget_show( _impl->_compUI[i]._btn ); - - attachToGridOrTable(t, _impl->_compUI[i]._btn, 2, row, 1, 1, false, true); - - _impl->_compUI[i]._map = g_new( guchar, 4 * 1024 ); - memset( _impl->_compUI[i]._map, 0x0ff, 1024 * 4 ); - - - // Signals - g_signal_connect( G_OBJECT( _impl->_compUI[i]._adj ), "value_changed", G_CALLBACK( ColorICCSelectorImpl::_adjustmentChanged ), _csel ); - - g_signal_connect( G_OBJECT( _impl->_compUI[i]._slider ), "grabbed", G_CALLBACK( ColorICCSelectorImpl::_sliderGrabbed ), _csel ); - g_signal_connect( G_OBJECT( _impl->_compUI[i]._slider ), "released", G_CALLBACK( ColorICCSelectorImpl::_sliderReleased ), _csel ); - g_signal_connect( G_OBJECT( _impl->_compUI[i]._slider ), "changed", G_CALLBACK( ColorICCSelectorImpl::_sliderChanged ), _csel ); - - row++; - } - - // Label - _impl->_label = gtk_label_new_with_mnemonic(_("_A:")); - gtk_misc_set_alignment(GTK_MISC(_impl->_label), 1.0, 0.5); - gtk_widget_show(_impl->_label); - - attachToGridOrTable(t, _impl->_label, 0, row, 1, 1); - - // Adjustment - _impl->_adj = GTK_ADJUSTMENT(gtk_adjustment_new(0.0, 0.0, 255.0, 1.0, 10.0, 10.0)); - - // Slider - _impl->_slider = sp_color_slider_new(_impl->_adj); - gtk_widget_set_tooltip_text(_impl->_slider, _("Alpha (opacity)")); - gtk_widget_show(_impl->_slider); - - attachToGridOrTable(t, _impl->_slider, 1, row, 1, 1, true); - - sp_color_slider_set_colors( SP_COLOR_SLIDER( _impl->_slider ), - SP_RGBA32_F_COMPOSE( 1.0, 1.0, 1.0, 0.0 ), - SP_RGBA32_F_COMPOSE( 1.0, 1.0, 1.0, 0.5 ), - SP_RGBA32_F_COMPOSE( 1.0, 1.0, 1.0, 1.0 ) ); - - - // Spinbutton - _impl->_sbtn = gtk_spin_button_new(GTK_ADJUSTMENT(_impl->_adj), 1.0, 0); - gtk_widget_set_tooltip_text(_impl->_sbtn, _("Alpha (opacity)")); - sp_dialog_defocus_on_enter(_impl->_sbtn); - gtk_label_set_mnemonic_widget(GTK_LABEL(_impl->_label), _impl->_sbtn); - gtk_widget_show(_impl->_sbtn); - - attachToGridOrTable(t, _impl->_sbtn, 2, row, 1, 1, false, true); - - // Signals - g_signal_connect(G_OBJECT(_impl->_adj), "value_changed", G_CALLBACK(ColorICCSelectorImpl::_adjustmentChanged), _csel); - - g_signal_connect(G_OBJECT(_impl->_slider), "grabbed", G_CALLBACK(ColorICCSelectorImpl::_sliderGrabbed), _csel); - g_signal_connect(G_OBJECT(_impl->_slider), "released", G_CALLBACK(ColorICCSelectorImpl::_sliderReleased), _csel); - g_signal_connect(G_OBJECT(_impl->_slider), "changed", G_CALLBACK(ColorICCSelectorImpl::_sliderChanged), _csel); -} - -static void sp_color_icc_selector_dispose(GObject *object) -{ - if (G_OBJECT_CLASS(sp_color_icc_selector_parent_class)->dispose) { - G_OBJECT_CLASS(sp_color_icc_selector_parent_class)->dispose(object); - } -} - -static void -sp_color_icc_selector_show_all (GtkWidget *widget) -{ - gtk_widget_show (widget); -} - -static void sp_color_icc_selector_hide(GtkWidget *widget) -{ - gtk_widget_hide(widget); -} - -GtkWidget * -sp_color_icc_selector_new (void) -{ - SPColorICCSelector *csel; - - csel = static_cast<SPColorICCSelector*>(g_object_new (SP_TYPE_COLOR_ICC_SELECTOR, NULL)); - - return GTK_WIDGET (csel); -} - - -void ColorICCSelectorImpl::_fixupHit( GtkWidget* /*src*/, gpointer data ) -{ - ColorICCSelectorImpl* self = reinterpret_cast<ColorICCSelectorImpl*>(data); - gtk_widget_set_sensitive( self->_fixupBtn, FALSE ); - self->_adjustmentChanged( self->_compUI[0]._adj, SP_COLOR_ICC_SELECTOR(self->_owner->_csel) ); -} - -#if defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) -void ColorICCSelectorImpl::_profileSelected( GtkWidget* /*src*/, gpointer data ) -{ - ColorICCSelectorImpl* self = reinterpret_cast<ColorICCSelectorImpl*>(data); - - GtkTreeIter iter; - if (gtk_combo_box_get_active_iter(GTK_COMBO_BOX(self->_profileSel), &iter)) { - GtkTreeModel *store = gtk_combo_box_get_model(GTK_COMBO_BOX(self->_profileSel)); - gchar* name = 0; - - gtk_tree_model_get(store, &iter, 1, &name, -1); - self->_switchToProfile( name ); - gtk_widget_set_tooltip_text(self->_profileSel, name ); - - if ( name ) { - g_free( name ); - } - } -} -#endif // defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) - -#if defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) -void ColorICCSelectorImpl::_switchToProfile( gchar const* name ) -{ - bool dirty = false; - SPColor tmp( _owner->_color ); - - if ( name ) { - if ( tmp.icc && tmp.icc->colorProfile == name ) { -#ifdef DEBUG_LCMS - g_message("Already at name [%s]", name ); -#endif // DEBUG_LCMS - } else { -#ifdef DEBUG_LCMS - g_message("Need to switch to profile [%s]", name ); -#endif // DEBUG_LCMS - if ( tmp.icc ) { - tmp.icc->colors.clear(); - } else { - tmp.icc = new SVGICCColor(); - } - tmp.icc->colorProfile = name; - Inkscape::ColorProfile* newProf = SP_ACTIVE_DOCUMENT->profileManager->find(name); - if ( newProf ) { - cmsHTRANSFORM trans = newProf->getTransfFromSRGB8(); - if ( trans ) { - guint32 val = _owner->_color.toRGBA32(0); - guchar pre[4] = { - static_cast<guchar>(SP_RGBA32_R_U(val)), - static_cast<guchar>(SP_RGBA32_G_U(val)), - static_cast<guchar>(SP_RGBA32_B_U(val)), - 255}; -#ifdef DEBUG_LCMS - g_message("Shoving in [%02x] [%02x] [%02x]", pre[0], pre[1], pre[2]); -#endif // DEBUG_LCMS - cmsUInt16Number post[4] = {0,0,0,0}; - cmsDoTransform( trans, pre, post, 1 ); -#ifdef DEBUG_LCMS - g_message("got on out [%04x] [%04x] [%04x] [%04x]", post[0], post[1], post[2], post[3]); -#endif // DEBUG_LCMS -#if HAVE_LIBLCMS1 - guint count = _cmsChannelsOf( asICColorSpaceSig(newProf->getColorSpace()) ); -#elif HAVE_LIBLCMS2 - guint count = cmsChannelsOf( asICColorSpaceSig(newProf->getColorSpace()) ); -#endif - - std::vector<colorspace::Component> things = colorspace::getColorSpaceInfo(asICColorSpaceSig(newProf->getColorSpace())); - - for ( guint i = 0; i < count; i++ ) { - gdouble val = (((gdouble)post[i])/65535.0) * (gdouble)((i < things.size()) ? things[i].scale : 1); -#ifdef DEBUG_LCMS - g_message(" scaled %d by %d to be %f", i, ((i < things.size()) ? things[i].scale : 1), val); -#endif // DEBUG_LCMS - tmp.icc->colors.push_back(val); - } - cmsHTRANSFORM retrans = newProf->getTransfToSRGB8(); - if ( retrans ) { - cmsDoTransform( retrans, post, pre, 1 ); -#ifdef DEBUG_LCMS - g_message(" back out [%02x] [%02x] [%02x]", pre[0], pre[1], pre[2]); -#endif // DEBUG_LCMS - tmp.set(SP_RGBA32_U_COMPOSE(pre[0], pre[1], pre[2], 0xff)); - } - } - } - dirty = true; - } - } else { -#ifdef DEBUG_LCMS - g_message("NUKE THE ICC"); -#endif // DEBUG_LCMS - if ( tmp.icc ) { - delete tmp.icc; - tmp.icc = 0; - dirty = true; - _fixupHit( 0, this ); - } else { -#ifdef DEBUG_LCMS - g_message("No icc to nuke"); -#endif // DEBUG_LCMS - } - } - - if ( dirty ) { -#ifdef DEBUG_LCMS - g_message("+----------------"); - g_message("+ new color is [%s]", tmp.toString().c_str()); -#endif // DEBUG_LCMS - _setProfile( tmp.icc ); - //_adjustmentChanged( _compUI[0]._adj, SP_COLOR_ICC_SELECTOR(_csel) ); - _owner->setColorAlpha( tmp, _owner->_alpha, true ); -#ifdef DEBUG_LCMS - g_message("+_________________"); -#endif // DEBUG_LCMS - } -} -#endif // defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) - -#if defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) -void ColorICCSelectorImpl::_profilesChanged( std::string const & name ) -{ - GtkComboBox* combo = GTK_COMBO_BOX(_profileSel); - - g_signal_handler_block( G_OBJECT(_profileSel), _profChangedID ); - - GtkListStore *store = GTK_LIST_STORE(gtk_combo_box_get_model(combo)); - gtk_list_store_clear(store); - - GtkTreeIter iter; - gtk_list_store_append (store, &iter); - gtk_list_store_set(store, &iter, 0, _("<none>"), 1, _("<none>"), -1); - - gtk_combo_box_set_active( combo, 0 ); - - int index = 1; - const GSList *current = SP_ACTIVE_DOCUMENT->getResourceList( "iccprofile" ); - while ( current ) { - SPObject* obj = SP_OBJECT(current->data); - Inkscape::ColorProfile* prof = reinterpret_cast<Inkscape::ColorProfile*>(obj); - - gtk_list_store_append (store, &iter); - gtk_list_store_set(store, &iter, 0, gr_ellipsize_text(prof->name, 25).c_str(), 1, prof->name, -1); - - if ( name == prof->name ) { - gtk_combo_box_set_active( combo, index ); - gtk_widget_set_tooltip_text(_profileSel, prof->name ); - } - - index++; - current = g_slist_next(current); - } - - g_signal_handler_unblock( G_OBJECT(_profileSel), _profChangedID ); -} -#else -void ColorICCSelectorImpl::_profilesChanged( std::string const & /*name*/ ) -{ -} -#endif // defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) - -// Helpers for setting color value - -void ColorICCSelector::_colorChanged() -{ - _impl->_updating = TRUE; - //sp_color_icc_set_color( SP_COLOR_ICC( _icc ), &color ); - -#ifdef DEBUG_LCMS - g_message( "/^^^^^^^^^ %p::_colorChanged(%08x:%s)", this, - _color.toRGBA32(_alpha), ( (_color.icc) ? _color.icc->colorProfile.c_str(): "<null>" ) - ); -#endif // DEBUG_LCMS - -#ifdef DEBUG_LCMS - g_message("FLIPPIES!!!! %p '%s'", _color.icc, (_color.icc ? _color.icc->colorProfile.c_str():"<null>")); -#endif // DEBUG_LCMS - - _impl->_profilesChanged( (_color.icc) ? _color.icc->colorProfile : std::string("") ); - ColorScales::setScaled( _impl->_adj, _alpha ); - -#if defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) - _impl->_setProfile( _color.icc ); - _impl->_fixupNeeded = 0; - gtk_widget_set_sensitive( _impl->_fixupBtn, FALSE ); - - if (_impl->_prof) { - if (_impl->_prof->getTransfToSRGB8() ) { - cmsUInt16Number tmp[4]; - for ( guint i = 0; i < _impl->_profChannelCount; i++ ) { - gdouble val = 0.0; - if ( _color.icc->colors.size() > i ) { - if ( _impl->_compUI[i]._component.scale == 256 ) { - val = (_color.icc->colors[i] + 128.0) / static_cast<gdouble>(_impl->_compUI[i]._component.scale); - } else { - val = _color.icc->colors[i] / static_cast<gdouble>(_impl->_compUI[i]._component.scale); - } - } - tmp[i] = val * 0x0ffff; - } - guchar post[4] = {0,0,0,0}; - cmsHTRANSFORM trans = _impl->_prof->getTransfToSRGB8(); - if ( trans ) { - cmsDoTransform( trans, tmp, post, 1 ); - guint32 other = SP_RGBA32_U_COMPOSE(post[0], post[1], post[2], 255 ); - if ( other != _color.toRGBA32(255) ) { - _impl->_fixupNeeded = other; - gtk_widget_set_sensitive( _impl->_fixupBtn, TRUE ); -#ifdef DEBUG_LCMS - g_message("Color needs to change 0x%06x to 0x%06x", _color.toRGBA32(255) >> 8, other >> 8 ); -#endif // DEBUG_LCMS - } - } - } - } -#else - //(void)color; -#endif // defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) - _impl->_updateSliders( -1 ); - - - _impl->_updating = FALSE; -#ifdef DEBUG_LCMS - g_message( "\\_________ %p::_colorChanged()", this ); -#endif // DEBUG_LCMS -} - -#if defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) -void ColorICCSelectorImpl::_setProfile( SVGICCColor* profile ) -{ -#ifdef DEBUG_LCMS - g_message( "/^^^^^^^^^ %p::_setProfile(%s)", this, - ( (profile) ? profile->colorProfile.c_str() : "<null>") - ); -#endif // DEBUG_LCMS - bool profChanged = false; - if ( _prof && (!profile || (_profileName != profile->colorProfile) ) ) { - // Need to clear out the prior one - profChanged = true; - _profileName.clear(); - _prof = 0; - _profChannelCount = 0; - } else if ( profile && !_prof ) { - profChanged = true; - } - - for ( size_t i = 0; i < _compUI.size(); i++ ) { - gtk_widget_hide( _compUI[i]._label ); - gtk_widget_hide( _compUI[i]._slider ); - gtk_widget_hide( _compUI[i]._btn ); - } - - if ( profile ) { - _prof = SP_ACTIVE_DOCUMENT->profileManager->find(profile->colorProfile.c_str()); - if ( _prof && (asICColorProfileClassSig(_prof->getProfileClass()) != cmsSigNamedColorClass) ) { -#if HAVE_LIBLCMS1 - _profChannelCount = _cmsChannelsOf( asICColorSpaceSig(_prof->getColorSpace()) ); -#elif HAVE_LIBLCMS2 - _profChannelCount = cmsChannelsOf( asICColorSpaceSig(_prof->getColorSpace()) ); -#endif - - if ( profChanged ) { - std::vector<colorspace::Component> things = colorspace::getColorSpaceInfo(asICColorSpaceSig(_prof->getColorSpace())); - for (size_t i = 0; (i < things.size()) && (i < _profChannelCount); ++i) - { - _compUI[i]._component = things[i]; - } - - for ( guint i = 0; i < _profChannelCount; i++ ) { - gtk_label_set_text_with_mnemonic( GTK_LABEL(_compUI[i]._label), (i < things.size()) ? things[i].name.c_str() : ""); - - gtk_widget_set_tooltip_text( _compUI[i]._slider, (i < things.size()) ? things[i].tip.c_str() : "" ); - gtk_widget_set_tooltip_text( _compUI[i]._btn, (i < things.size()) ? things[i].tip.c_str() : "" ); - - sp_color_slider_set_colors( SP_COLOR_SLIDER(_compUI[i]._slider), - SPColor(0.0, 0.0, 0.0).toRGBA32(0xff), - SPColor(0.5, 0.5, 0.5).toRGBA32(0xff), - SPColor(1.0, 1.0, 1.0).toRGBA32(0xff) ); -/* - _compUI[i]._adj = GTK_ADJUSTMENT( gtk_adjustment_new( val, 0.0, _fooScales[i], step, page, page ) ); - g_signal_connect( G_OBJECT( _compUI[i]._adj ), "value_changed", G_CALLBACK( _adjustmentChanged ), _csel ); - - sp_color_slider_set_adjustment( SP_COLOR_SLIDER(_compUI[i]._slider), _compUI[i]._adj ); - gtk_spin_button_set_adjustment( GTK_SPIN_BUTTON(_compUI[i]._btn), _compUI[i]._adj ); - gtk_spin_button_set_digits( GTK_SPIN_BUTTON(_compUI[i]._btn), digits ); -*/ - gtk_widget_show( _compUI[i]._label ); - gtk_widget_show( _compUI[i]._slider ); - gtk_widget_show( _compUI[i]._btn ); - //gtk_adjustment_set_value( _compUI[i]._adj, 0.0 ); - //gtk_adjustment_set_value( _compUI[i]._adj, val ); - } - for ( size_t i = _profChannelCount; i < _compUI.size(); i++ ) { - gtk_widget_hide( _compUI[i]._label ); - gtk_widget_hide( _compUI[i]._slider ); - gtk_widget_hide( _compUI[i]._btn ); - } - } - } else { - // Give up for now on named colors - _prof = 0; - } - } - -#ifdef DEBUG_LCMS - g_message( "\\_________ %p::_setProfile()", this ); -#endif // DEBUG_LCMS -} -#endif // defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) - -void ColorICCSelectorImpl::_updateSliders( gint ignore ) -{ -#if defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) - if ( _owner->_color.icc ) - { - for ( guint i = 0; i < _profChannelCount; i++ ) { - gdouble val = 0.0; - if ( _owner->_color.icc->colors.size() > i ) { - if ( _compUI[i]._component.scale == 256 ) { - val = (_owner->_color.icc->colors[i] + 128.0) / static_cast<gdouble>(_compUI[i]._component.scale); - } else { - val = _owner->_color.icc->colors[i] / static_cast<gdouble>(_compUI[i]._component.scale); - } - } - gtk_adjustment_set_value( _compUI[i]._adj, val ); - } - - if ( _prof ) { - if ( _prof->getTransfToSRGB8() ) { - for ( guint i = 0; i < _profChannelCount; i++ ) { - if ( static_cast<gint>(i) != ignore ) { - cmsUInt16Number* scratch = getScratch(); - cmsUInt16Number filler[4] = {0, 0, 0, 0}; - for ( guint j = 0; j < _profChannelCount; j++ ) { - filler[j] = 0x0ffff * ColorScales::getScaled( _compUI[j]._adj ); - } - - cmsUInt16Number* p = scratch; - for ( guint x = 0; x < 1024; x++ ) { - for ( guint j = 0; j < _profChannelCount; j++ ) { - if ( j == i ) { - *p++ = x * 0x0ffff / 1024; - } else { - *p++ = filler[j]; - } - } - } - - cmsHTRANSFORM trans = _prof->getTransfToSRGB8(); - if ( trans ) { - cmsDoTransform( trans, scratch, _compUI[i]._map, 1024 ); - sp_color_slider_set_map( SP_COLOR_SLIDER(_compUI[i]._slider), _compUI[i]._map ); - } - } - } - } - } - } -#else - (void)ignore; -#endif // defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) - - guint32 start = _owner->_color.toRGBA32( 0x00 ); - guint32 mid = _owner->_color.toRGBA32( 0x7f ); - guint32 end = _owner->_color.toRGBA32( 0xff ); - - sp_color_slider_set_colors( SP_COLOR_SLIDER(_slider), start, mid, end ); -} - - -void ColorICCSelectorImpl::_adjustmentChanged( GtkAdjustment *adjustment, SPColorICCSelector *cs ) -{ -// // TODO check this. It looks questionable: -// // if a value is entered between 0 and 1 exclusive, normalize it to (int) 0..255 or 0..100 -// if (adjustment->value > 0.0 && adjustment->value < 1.0) { -// gtk_adjustment_set_value( adjustment, floor ((adjustment->value) * adjustment->upper + 0.5) ); -// } - -#ifdef DEBUG_LCMS - g_message( "/^^^^^^^^^ %p::_adjustmentChanged()", cs ); -#endif // DEBUG_LCMS - - ColorICCSelector* iccSelector = static_cast<ColorICCSelector*>(SP_COLOR_SELECTOR(cs)->base); - if (iccSelector->_impl->_updating) { - return; - } - - iccSelector->_impl->_updating = TRUE; - - gint match = -1; - - SPColor newColor( iccSelector->_color ); - gfloat scaled = ColorScales::getScaled( iccSelector->_impl->_adj ); - if ( iccSelector->_impl->_adj == adjustment ) { -#ifdef DEBUG_LCMS - g_message("ALPHA"); -#endif // DEBUG_LCMS - } else { -#if defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) - for ( size_t i = 0; i < iccSelector->_impl->_compUI.size(); i++ ) { - if ( iccSelector->_impl->_compUI[i]._adj == adjustment ) { - match = i; - break; - } - } - if ( match >= 0 ) { -#ifdef DEBUG_LCMS - g_message(" channel %d", match ); -#endif // DEBUG_LCMS - } - - - cmsUInt16Number tmp[4]; - for ( guint i = 0; i < 4; i++ ) { - tmp[i] = ColorScales::getScaled( iccSelector->_impl->_compUI[i]._adj ) * 0x0ffff; - } - guchar post[4] = {0,0,0,0}; - - cmsHTRANSFORM trans = iccSelector->_impl->_prof->getTransfToSRGB8(); - if ( trans ) { - cmsDoTransform( trans, tmp, post, 1 ); - } - - SPColor other( SP_RGBA32_U_COMPOSE(post[0], post[1], post[2], 255) ); - other.icc = new SVGICCColor(); - if ( iccSelector->_color.icc ) { - other.icc->colorProfile = iccSelector->_color.icc->colorProfile; - } - - guint32 prior = iccSelector->_color.toRGBA32(255); - guint32 newer = other.toRGBA32(255); - - if ( prior != newer ) { -#ifdef DEBUG_LCMS - g_message("Transformed color from 0x%08x to 0x%08x", prior, newer ); - g_message(" ~~~~ FLIP"); -#endif // DEBUG_LCMS - newColor = other; - newColor.icc->colors.clear(); - for ( guint i = 0; i < iccSelector->_impl->_profChannelCount; i++ ) { - gdouble val = ColorScales::getScaled( iccSelector->_impl->_compUI[i]._adj ); - val *= iccSelector->_impl->_compUI[i]._component.scale; - if ( iccSelector->_impl->_compUI[i]._component.scale == 256 ) { - val -= 128; - } - newColor.icc->colors.push_back( val ); - } - } -#endif // defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) - } - iccSelector->_updateInternals( newColor, scaled, iccSelector->_impl->_dragging ); - iccSelector->_impl->_updateSliders( match ); - - iccSelector->_impl->_updating = FALSE; -#ifdef DEBUG_LCMS - g_message( "\\_________ %p::_adjustmentChanged()", cs ); -#endif // DEBUG_LCMS -} - -void ColorICCSelectorImpl::_sliderGrabbed( SPColorSlider * /*slider*/, SPColorICCSelector * /*cs*/ ) -{ -// ColorICCSelector* iccSelector = dynamic_cast<ColorICCSelector*>(SP_COLOR_SELECTOR(cs)->base); -// if (!iccSelector->_dragging) { -// iccSelector->_dragging = TRUE; -// iccSelector->_grabbed(); -// iccSelector->_updateInternals( iccSelector->_color, ColorScales::getScaled( iccSelector->_impl->_adj ), iccSelector->_dragging ); -// } -} - -void ColorICCSelectorImpl::_sliderReleased( SPColorSlider * /*slider*/, SPColorICCSelector * /*cs*/ ) -{ -// ColorICCSelector* iccSelector = dynamic_cast<ColorICCSelector*>(SP_COLOR_SELECTOR(cs)->base); -// if (iccSelector->_dragging) { -// iccSelector->_dragging = FALSE; -// iccSelector->_released(); -// iccSelector->_updateInternals( iccSelector->_color, ColorScales::getScaled( iccSelector->_adj ), iccSelector->_dragging ); -// } -} - -#ifdef DEBUG_LCMS -void ColorICCSelectorImpl::_sliderChanged( SPColorSlider *slider, SPColorICCSelector *cs ) -#else -void ColorICCSelectorImpl::_sliderChanged( SPColorSlider * /*slider*/, SPColorICCSelector * /*cs*/ ) -#endif // DEBUG_LCMS -{ -#ifdef DEBUG_LCMS - g_message("Changed %p and %p", slider, cs ); -#endif // DEBUG_LCMS -// ColorICCSelector* iccSelector = dynamic_cast<ColorICCSelector*>(SP_COLOR_SELECTOR(cs)->base); - -// iccSelector->_updateInternals( iccSelector->_color, ColorScales::getScaled( iccSelector->_adj ), iccSelector->_dragging ); -} - - -/* - 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/widgets/sp-color-icc-selector.h b/src/widgets/sp-color-icc-selector.h deleted file mode 100644 index 6cdaff639..000000000 --- a/src/widgets/sp-color-icc-selector.h +++ /dev/null @@ -1,72 +0,0 @@ -#ifndef SEEN_SP_COLOR_ICC_SELECTOR_H -#define SEEN_SP_COLOR_ICC_SELECTOR_H - -#include <glib.h> -#include "sp-color-selector.h" - -namespace Inkscape { -class ColorProfile; -} - -struct SPColorICCSelector; -struct SPColorICCSelectorClass; - -class ColorICCSelectorImpl; - -class ColorICCSelector: public ColorSelector -{ -public: - ColorICCSelector( SPColorSelector* csel ); - virtual ~ColorICCSelector(); - - virtual void init(); - -protected: - virtual void _colorChanged(); - - void _recalcColor( gboolean changing ); - -private: - friend class ColorICCSelectorImpl; - - // By default, disallow copy constructor and assignment operator - ColorICCSelector( const ColorICCSelector& obj ); - ColorICCSelector& operator=( const ColorICCSelector& obj ); - - ColorICCSelectorImpl *_impl; -}; - - - -#define SP_TYPE_COLOR_ICC_SELECTOR (sp_color_icc_selector_get_type()) -#define SP_COLOR_ICC_SELECTOR(o) (G_TYPE_CHECK_INSTANCE_CAST((o), SP_TYPE_COLOR_ICC_SELECTOR, SPColorICCSelector)) -#define SP_COLOR_ICC_SELECTOR_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), SP_TYPE_COLOR_ICC_SELECTOR, SPColorICCSelectorClass)) -#define SP_IS_COLOR_ICC_SELECTOR(o) (G_TYPE_CHECK_INSTANCE_TYPE((o), SP_TYPE_COLOR_ICC_SELECTOR)) -#define SP_IS_COLOR_ICC_SELECTOR_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE((k), SP_TYPE_COLOR_ICC_SELECTOR)) - -struct SPColorICCSelector { - SPColorSelector parent; -}; - -struct SPColorICCSelectorClass { - SPColorSelectorClass parent_class; -}; - -GType sp_color_icc_selector_get_type(void); - -GtkWidget *sp_color_icc_selector_new(void); - - - -#endif // SEEN_SP_COLOR_ICC_SELECTOR_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/widgets/sp-color-notebook.cpp b/src/widgets/sp-color-notebook.cpp deleted file mode 100644 index c7fa96efd..000000000 --- a/src/widgets/sp-color-notebook.cpp +++ /dev/null @@ -1,826 +0,0 @@ -/* - * A notebook with RGB, CMYK, CMS, HSL, and Wheel pages - * - * Author: - * Lauris Kaplinski <lauris@kaplinski.com> - * bulia byak <buliabyak@users.sf.net> - * - * Copyright (C) 2001-2002 Lauris Kaplinski - * - * This code is in public domain - */ - -#undef SPCS_PREVIEW -#define noDUMP_CHANGE_INFO - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include "widgets/icon.h" -#include <cstring> -#include <string> -#include <cstdlib> -#include <cstddef> -#include <gtk/gtk.h> -#include <glibmm/i18n.h> - -#include "ui/dialog-events.h" -#include "../preferences.h" -#include "sp-color-notebook.h" -#include "spw-utilities.h" -#include "sp-color-scales.h" -#include "sp-color-icc-selector.h" -#include "sp-color-wheel-selector.h" -#include "svg/svg-icc-color.h" -#include "../inkscape.h" -#include "../document.h" -#include "../profile-manager.h" -#include "color-profile.h" -#include "cms-system.h" -#include "ui/tools-switch.h" -#include "ui/tools/tool-base.h" - -using Inkscape::CMSSystem; - -struct SPColorNotebookTracker { - const gchar* name; - const gchar* className; - GType type; - guint submode; - gboolean enabledFull; - gboolean enabledBrief; - SPColorNotebook *backPointer; -}; - -static void sp_color_notebook_dispose(GObject *object); - -static void sp_color_notebook_show_all (GtkWidget *widget); -static void sp_color_notebook_hide(GtkWidget *widget); - -#define XPAD 4 -#define YPAD 1 - -G_DEFINE_TYPE(SPColorNotebook, sp_color_notebook, SP_TYPE_COLOR_SELECTOR); - -static void sp_color_notebook_class_init(SPColorNotebookClass *klass) -{ - GObjectClass *object_class = reinterpret_cast<GObjectClass *>(klass); - GtkWidgetClass *widget_class = reinterpret_cast<GtkWidgetClass *>(klass); - - object_class->dispose = sp_color_notebook_dispose; - - widget_class->show_all = sp_color_notebook_show_all; - widget_class->hide = sp_color_notebook_hide; -} - -static void -sp_color_notebook_switch_page(GtkNotebook *notebook, - GtkWidget *page, - guint page_num, - SPColorNotebook *colorbook) -{ - if ( colorbook ) - { - ColorNotebook* nb = dynamic_cast<ColorNotebook*>(SP_COLOR_SELECTOR(colorbook)->base); - nb->switchPage( notebook, page, page_num ); - - // remember the page we switched to - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - prefs->setInt("/colorselector/page", page_num); - } -} - -void ColorNotebook::switchPage(GtkNotebook*, - GtkWidget*, - guint page_num) -{ - SPColorSelector* csel; - GtkWidget* widget; - - if ( gtk_notebook_get_current_page (GTK_NOTEBOOK (_book)) >= 0 ) - { - csel = getCurrentSelector(); - csel->base->getColorAlpha(_color, _alpha); - } - widget = gtk_notebook_get_nth_page (GTK_NOTEBOOK (_book), page_num); - if ( widget && SP_IS_COLOR_SELECTOR(widget) ) - { - csel = SP_COLOR_SELECTOR (widget); - csel->base->setColorAlpha( _color, _alpha ); - - // Temporary workaround to undo a spurious GRABBED - _released(); - } -} - -static gint sp_color_notebook_menu_handler( GtkWidget *widget, GdkEvent *event ) -{ - if (event->type == GDK_BUTTON_PRESS) - { - SPColorSelector* csel = SP_COLOR_SELECTOR(widget); - (dynamic_cast<ColorNotebook*>(csel->base))->menuHandler( event ); - - /* Tell calling code that we have handled this event; the buck - * stops here. */ - return TRUE; - } - - /* Tell calling code that we have not handled this event; pass it on. */ - return FALSE; -} - -gint ColorNotebook::menuHandler( GdkEvent* event ) -{ - GdkEventButton *bevent = (GdkEventButton *) event; - gtk_menu_popup (GTK_MENU( _popup ), NULL, NULL, NULL, NULL, - bevent->button, bevent->time); - return TRUE; -} - -static void sp_color_notebook_menuitem_response (GtkMenuItem *menuitem, gpointer user_data) -{ - gboolean active = gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (menuitem)); - SPColorNotebookTracker *entry = reinterpret_cast< SPColorNotebookTracker* > (user_data); - if ( entry ) - { - if ( active ) - { - (dynamic_cast<ColorNotebook*>(SP_COLOR_SELECTOR(entry->backPointer)->base))->addPage(entry->type, entry->submode); - } - else - { - (dynamic_cast<ColorNotebook*>(SP_COLOR_SELECTOR(entry->backPointer)->base))->removePage(entry->type, entry->submode); - } - } -} - -static void -sp_color_notebook_init (SPColorNotebook *colorbook) -{ - SP_COLOR_SELECTOR(colorbook)->base = new ColorNotebook( SP_COLOR_SELECTOR(colorbook) ); - - if ( SP_COLOR_SELECTOR(colorbook)->base ) - { - SP_COLOR_SELECTOR(colorbook)->base->init(); - } -} - -void ColorNotebook::init() -{ - guint row = 0; - guint i = 0; - guint j = 0; - GType *selector_types = 0; - guint selector_type_count = 0; - - /* tempory hardcoding to get types loaded */ - SP_TYPE_COLOR_SCALES; - SP_TYPE_COLOR_WHEEL_SELECTOR; -#if defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) - SP_TYPE_COLOR_ICC_SELECTOR; -#endif // defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) - - /* REJON: Comment out the next line to not use the normal GTK Color - wheel. */ - -// SP_TYPE_COLOR_GTKSELECTOR; - - _updating = FALSE; - _updatingrgba = FALSE; - _btn = 0; - _popup = 0; - _trackerList = g_ptr_array_new (); - - _book = gtk_notebook_new (); - gtk_widget_show (_book); - - // Dont show the notebook tabs, use radiobuttons instead - gtk_notebook_set_show_border (GTK_NOTEBOOK (_book), false); - gtk_notebook_set_show_tabs (GTK_NOTEBOOK (_book), false); - - selector_types = g_type_children (SP_TYPE_COLOR_SELECTOR, &selector_type_count); - - for ( i = 0; i < selector_type_count; i++ ) - { - if (!g_type_is_a (selector_types[i], SP_TYPE_COLOR_NOTEBOOK)) - { - guint howmany = 1; - gpointer klass = g_type_class_ref (selector_types[i]); - if ( klass && SP_IS_COLOR_SELECTOR_CLASS(klass) ) - { - SPColorSelectorClass *ck = SP_COLOR_SELECTOR_CLASS (klass); - howmany = MAX (1, ck->submode_count); - for ( j = 0; j < howmany; j++ ) - { - SPColorNotebookTracker *entry = reinterpret_cast< SPColorNotebookTracker* > (malloc(sizeof(SPColorNotebookTracker))); - if ( entry ) - { - memset( entry, 0, sizeof(SPColorNotebookTracker) ); - entry->name = ck->name[j]; - entry->type = selector_types[i]; - entry->submode = j; - entry->enabledFull = TRUE; - entry->enabledBrief = TRUE; - entry->backPointer = SP_COLOR_NOTEBOOK(_csel); - - g_ptr_array_add (_trackerList, entry); - } - } - } - } - } - -#if GTK_CHECK_VERSION(3,0,0) - _buttonbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 2); - gtk_box_set_homogeneous(GTK_BOX(_buttonbox), TRUE); -#else - _buttonbox = gtk_hbox_new (TRUE, 2); -#endif - - gtk_widget_show (_buttonbox); - _buttons = new GtkWidget *[_trackerList->len]; - - for ( i = 0; i < _trackerList->len; i++ ) - { - SPColorNotebookTracker *entry = - reinterpret_cast< SPColorNotebookTracker* > (g_ptr_array_index (_trackerList, i)); - if ( entry ) - { - addPage(entry->type, entry->submode); - } - } - -#if GTK_CHECK_VERSION(3,0,0) - GtkWidget* table = gtk_grid_new(); -#else - GtkWidget* table = gtk_table_new(2, 3, FALSE); -#endif - - gtk_widget_show (table); - - gtk_box_pack_start (GTK_BOX (_csel), table, TRUE, TRUE, 0); - - sp_set_font_size_smaller (_buttonbox); - -#if GTK_CHECK_VERSION(3,0,0) - #if GTK_CHECK_VERSION(3,12,0) - gtk_widget_set_margin_start(_buttonbox, XPAD); - gtk_widget_set_margin_end(_buttonbox, XPAD); - #else - gtk_widget_set_margin_left(_buttonbox, XPAD); - gtk_widget_set_margin_right(_buttonbox, XPAD); - #endif - gtk_widget_set_margin_top(_buttonbox, YPAD); - gtk_widget_set_margin_bottom(_buttonbox, YPAD); - gtk_widget_set_hexpand(_buttonbox, TRUE); - gtk_widget_set_valign(_buttonbox, GTK_ALIGN_CENTER); - gtk_grid_attach(GTK_GRID(table), _buttonbox, 0, row, 2, 1); -#else - gtk_table_attach (GTK_TABLE (table), _buttonbox, 0, 2, row, row + 1, - static_cast<GtkAttachOptions>(GTK_EXPAND|GTK_FILL), - static_cast<GtkAttachOptions>(0), - XPAD, YPAD); -#endif - - row++; - -#if GTK_CHECK_VERSION(3,0,0) - #if GTK_CHECK_VERSION(3,12,0) - gtk_widget_set_margin_start(_book, XPAD*2); - gtk_widget_set_margin_end(_book, XPAD*2); - #else - gtk_widget_set_margin_left(_book, XPAD*2); - gtk_widget_set_margin_right(_book, XPAD*2); - #endif - gtk_widget_set_margin_top(_book, YPAD); - gtk_widget_set_margin_bottom(_book, YPAD); - gtk_widget_set_hexpand(_book, TRUE); - gtk_widget_set_vexpand(_book, TRUE); - gtk_grid_attach(GTK_GRID(table), _book, 0, row, 2, 1); -#else - gtk_table_attach (GTK_TABLE (table), _book, 0, 2, row, row + 1, - static_cast<GtkAttachOptions>(GTK_EXPAND|GTK_FILL), - static_cast<GtkAttachOptions>(GTK_EXPAND|GTK_FILL), - XPAD*2, YPAD); -#endif - - // restore the last active page - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - _setCurrentPage(prefs->getInt("/colorselector/page", 0)); - - { - gboolean found = FALSE; - - _popup = gtk_menu_new(); - GtkMenu *menu = GTK_MENU (_popup); - - for ( i = 0; i < _trackerList->len; i++ ) - { - SPColorNotebookTracker *entry = reinterpret_cast< SPColorNotebookTracker* > (g_ptr_array_index (_trackerList, i)); - if ( entry ) - { - GtkWidget *item = gtk_check_menu_item_new_with_label (_(entry->name)); - gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), entry->enabledFull); - gtk_widget_show (item); - gtk_menu_shell_append (GTK_MENU_SHELL(menu), item); - - g_signal_connect (G_OBJECT (item), "activate", - G_CALLBACK (sp_color_notebook_menuitem_response), - reinterpret_cast< gpointer > (entry) ); - found = TRUE; - } - } - - GtkWidget *arrow = gtk_arrow_new(GTK_ARROW_RIGHT, GTK_SHADOW_NONE); - gtk_widget_show (arrow); - - _btn = gtk_button_new (); - gtk_widget_show (_btn); - gtk_container_add (GTK_CONTAINER (_btn), arrow); - - GtkWidget *align = gtk_alignment_new (1.0, 0.0, 0.0, 0.0); - gtk_widget_show (align); - gtk_container_add (GTK_CONTAINER (align), _btn); - - // uncomment to reenable the "show/hide modes" menu, - // but first fix it so it remembers its settings in prefs and does not take that much space (entire vertical column!) - //gtk_table_attach (GTK_TABLE (table), align, 2, 3, row, row + 1, GTK_FILL, GTK_FILL, XPAD, YPAD); - - g_signal_connect_swapped(G_OBJECT(_btn), "event", G_CALLBACK (sp_color_notebook_menu_handler), G_OBJECT(_csel)); - if ( !found ) - { - gtk_widget_set_sensitive (_btn, FALSE); - } - } - - row++; - -#if GTK_CHECK_VERSION(3,0,0) - GtkWidget *rgbabox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); -#else - GtkWidget *rgbabox = gtk_hbox_new (FALSE, 0); -#endif - -#if defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) - /* Create color management icons */ - _box_colormanaged = gtk_event_box_new (); - GtkWidget *colormanaged = gtk_image_new_from_icon_name ("color-management-icon", GTK_ICON_SIZE_SMALL_TOOLBAR); - gtk_container_add (GTK_CONTAINER (_box_colormanaged), colormanaged); - gtk_widget_set_tooltip_text (_box_colormanaged, _("Color Managed")); - gtk_widget_set_sensitive (_box_colormanaged, false); - gtk_box_pack_start(GTK_BOX(rgbabox), _box_colormanaged, FALSE, FALSE, 2); - - _box_outofgamut = gtk_event_box_new (); - GtkWidget *outofgamut = gtk_image_new_from_icon_name ("out-of-gamut-icon", GTK_ICON_SIZE_SMALL_TOOLBAR); - gtk_container_add (GTK_CONTAINER (_box_outofgamut), outofgamut); - gtk_widget_set_tooltip_text (_box_outofgamut, _("Out of gamut!")); - gtk_widget_set_sensitive (_box_outofgamut, false); - gtk_box_pack_start(GTK_BOX(rgbabox), _box_outofgamut, FALSE, FALSE, 2); - - _box_toomuchink = gtk_event_box_new (); - GtkWidget *toomuchink = gtk_image_new_from_icon_name ("too-much-ink-icon", GTK_ICON_SIZE_SMALL_TOOLBAR); - gtk_container_add (GTK_CONTAINER (_box_toomuchink), toomuchink); - gtk_widget_set_tooltip_text (_box_toomuchink, _("Too much ink!")); - gtk_widget_set_sensitive (_box_toomuchink, false); - gtk_box_pack_start(GTK_BOX(rgbabox), _box_toomuchink, FALSE, FALSE, 2); -#endif //defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) - - - /* Color picker */ - GtkWidget *picker = gtk_image_new_from_icon_name ("color-picker", GTK_ICON_SIZE_SMALL_TOOLBAR); - _btn_picker = gtk_button_new (); - gtk_button_set_relief(GTK_BUTTON(_btn_picker), GTK_RELIEF_NONE); - gtk_widget_show (_btn); - gtk_container_add (GTK_CONTAINER (_btn_picker), picker); - gtk_widget_set_tooltip_text (_btn_picker, _("Pick colors from image")); - gtk_box_pack_start(GTK_BOX(rgbabox), _btn_picker, FALSE, FALSE, 2); - g_signal_connect(G_OBJECT(_btn_picker), "clicked", G_CALLBACK(ColorNotebook::_picker_clicked), _csel); - - /* Create RGBA entry and color preview */ - _rgbal = gtk_label_new_with_mnemonic (_("RGBA_:")); - gtk_misc_set_alignment (GTK_MISC (_rgbal), 1.0, 0.5); - gtk_box_pack_start(GTK_BOX(rgbabox), _rgbal, TRUE, TRUE, 2); - - _rgbae = gtk_entry_new (); - sp_dialog_defocus_on_enter (_rgbae); - gtk_entry_set_max_length (GTK_ENTRY (_rgbae), 8); - gtk_entry_set_width_chars (GTK_ENTRY (_rgbae), 8); - gtk_widget_set_tooltip_text (_rgbae, _("Hexadecimal RGBA value of the color")); - gtk_box_pack_start(GTK_BOX(rgbabox), _rgbae, FALSE, FALSE, 0); - gtk_label_set_mnemonic_widget (GTK_LABEL(_rgbal), _rgbae); - - sp_set_font_size_smaller (rgbabox); - gtk_widget_show_all (rgbabox); - -#if defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) - //the "too much ink" icon is initially hidden - gtk_widget_hide(GTK_WIDGET(_box_toomuchink)); -#endif //defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) - -#if GTK_CHECK_VERSION(3,0,0) - #if GTK_CHECK_VERSION(3,12,0) - gtk_widget_set_margin_start(rgbabox, XPAD); - gtk_widget_set_margin_end(rgbabox, XPAD); - #else - gtk_widget_set_margin_left(rgbabox, XPAD); - gtk_widget_set_margin_right(rgbabox, XPAD); - #endif - gtk_widget_set_margin_top(rgbabox, YPAD); - gtk_widget_set_margin_bottom(rgbabox, YPAD); - gtk_grid_attach(GTK_GRID(table), rgbabox, 0, row, 2, 1); -#else - gtk_table_attach (GTK_TABLE (table), rgbabox, 0, 2, row, row + 1, GTK_FILL, GTK_SHRINK, XPAD, YPAD); -#endif - -#ifdef SPCS_PREVIEW - _p = sp_color_preview_new (0xffffffff); - gtk_widget_show (_p); - gtk_table_attach (GTK_TABLE (table), _p, 2, 3, row, row + 1, GTK_FILL, GTK_FILL, XPAD, YPAD); -#endif - - _switchId = g_signal_connect(G_OBJECT (_book), "switch-page", - G_CALLBACK (sp_color_notebook_switch_page), SP_COLOR_NOTEBOOK(_csel)); - - _entryId = g_signal_connect (G_OBJECT (_rgbae), "changed", G_CALLBACK (ColorNotebook::_rgbaEntryChangedHook), _csel); -} - -static void sp_color_notebook_dispose(GObject *object) -{ - if (G_OBJECT_CLASS(sp_color_notebook_parent_class)->dispose) - G_OBJECT_CLASS(sp_color_notebook_parent_class)->dispose(object); -} - -ColorNotebook::~ColorNotebook() -{ - if ( _trackerList ) - { - g_ptr_array_free (_trackerList, TRUE); - _trackerList = 0; - } - - if ( _switchId ) - { - if ( _book ) - { - g_signal_handler_disconnect (_book, _switchId); - _switchId = 0; - } - } - - if ( _buttons ) - { - delete [] _buttons; - _buttons = 0; - } - -} - -static void -sp_color_notebook_show_all (GtkWidget *widget) -{ - gtk_widget_show (widget); -} - -static void sp_color_notebook_hide(GtkWidget *widget) -{ - gtk_widget_hide(widget); -} - -GtkWidget *sp_color_notebook_new() -{ - SPColorNotebook *colorbook = SP_COLOR_NOTEBOOK(g_object_new (SP_TYPE_COLOR_NOTEBOOK, NULL)); - - return GTK_WIDGET(colorbook); -} - -ColorNotebook::ColorNotebook( SPColorSelector* csel ) - : ColorSelector( csel ) -{ -} - -SPColorSelector* ColorNotebook::getCurrentSelector() -{ - SPColorSelector* csel = NULL; - gint current_page = gtk_notebook_get_current_page (GTK_NOTEBOOK (_book)); - - if ( current_page >= 0 ) - { - GtkWidget* widget = gtk_notebook_get_nth_page (GTK_NOTEBOOK (_book), current_page); - if ( SP_IS_COLOR_SELECTOR (widget) ) - { - csel = SP_COLOR_SELECTOR (widget); - } - } - - return csel; -} - -void ColorNotebook::_colorChanged() -{ - SPColorSelector* cselPage = getCurrentSelector(); - if ( cselPage ) - { - cselPage->base->setColorAlpha( _color, _alpha ); - } - - _updateRgbaEntry( _color, _alpha ); -} - -void ColorNotebook::_picker_clicked(GtkWidget * /*widget*/, SPColorNotebook * /*colorbook*/) -{ - // Set the dropper into a "one click" mode, so it reverts to the previous tool after a click - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - prefs->setBool("/tools/dropper/onetimepick", true); - Inkscape::UI::Tools::sp_toggle_dropper(SP_ACTIVE_DESKTOP); -} - -void ColorNotebook::_rgbaEntryChangedHook(GtkEntry *entry, SPColorNotebook *colorbook) -{ - (dynamic_cast<ColorNotebook*>(SP_COLOR_SELECTOR(colorbook)->base))->_rgbaEntryChanged( entry ); -} - -void ColorNotebook::_rgbaEntryChanged(GtkEntry* entry) -{ - if (_updating) return; - if (_updatingrgba) return; - - const gchar *t = gtk_entry_get_text( entry ); - - if (t) { - Glib::ustring text = t; - bool changed = false; - if (!text.empty() && text[0] == '#') { - changed = true; - text.erase(0,1); - if (text.size() == 6) { - // it was a standard RGB hex - unsigned int alph = SP_COLOR_F_TO_U(_alpha); - gchar* tmp = g_strdup_printf("%02x", alph); - text += tmp; - g_free(tmp); - } - } - gchar* str = g_strdup(text.c_str()); - gchar* end = 0; - guint64 rgba = g_ascii_strtoull( str, &end, 16 ); - if ( end != str ) { - ptrdiff_t len = end - str; - if ( len < 8 ) { - rgba = rgba << ( 4 * ( 8 - len ) ); - } - _updatingrgba = TRUE; - if ( changed ) { - gtk_entry_set_text( entry, str ); - } - SPColor color( rgba ); - setColorAlpha( color, SP_RGBA32_A_F(rgba), true ); - _updatingrgba = FALSE; - } - g_free(str); - } -} - -// TODO pass in param so as to avoid the need for SP_ACTIVE_DOCUMENT -void ColorNotebook::_updateRgbaEntry( const SPColor& color, gfloat alpha ) -{ - g_return_if_fail( ( 0.0 <= alpha ) && ( alpha <= 1.0 ) ); - -#if defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) - /* update color management icon*/ - gtk_widget_set_sensitive (_box_colormanaged, color.icc != NULL); - - /* update out-of-gamut icon */ - gtk_widget_set_sensitive (_box_outofgamut, false); - if (color.icc){ - Inkscape::ColorProfile* target_profile = SP_ACTIVE_DOCUMENT->profileManager->find(color.icc->colorProfile.c_str()); - if ( target_profile ) - gtk_widget_set_sensitive(_box_outofgamut, target_profile->GamutCheck(color)); - } - - /* update too-much-ink icon */ - gtk_widget_set_sensitive (_box_toomuchink, false); - if (color.icc){ - Inkscape::ColorProfile* prof = SP_ACTIVE_DOCUMENT->profileManager->find(color.icc->colorProfile.c_str()); - if ( prof && CMSSystem::isPrintColorSpace(prof) ) { - gtk_widget_show(GTK_WIDGET(_box_toomuchink)); - double ink_sum = 0; - for (unsigned int i=0; i<color.icc->colors.size(); i++){ - ink_sum += color.icc->colors[i]; - } - - /* Some literature states that when the sum of paint values exceed 320%, it is considered to be a satured color, - which means the paper can get too wet due to an excessive ammount of ink. This may lead to several issues - such as misalignment and poor quality of printing in general.*/ - if ( ink_sum > 3.2 ) - gtk_widget_set_sensitive (_box_toomuchink, true); - } else { - gtk_widget_hide(GTK_WIDGET(_box_toomuchink)); - } - } -#endif //defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) - - if ( !_updatingrgba ) - { - gchar s[32]; - guint32 rgba; - - /* Update RGBA entry */ - rgba = color.toRGBA32( alpha ); - - g_snprintf (s, 32, "%08x", rgba); - const gchar* oldText = gtk_entry_get_text( GTK_ENTRY( _rgbae ) ); - if ( strcmp( oldText, s ) != 0 ) - { - g_signal_handler_block( _rgbae, _entryId ); - gtk_entry_set_text( GTK_ENTRY(_rgbae), s ); - g_signal_handler_unblock( _rgbae, _entryId ); - } - } -} - -void ColorNotebook::_setCurrentPage(int i) -{ - gtk_notebook_set_current_page(GTK_NOTEBOOK(_book), i); - - if (_buttons && _trackerList && (static_cast<size_t>(i) < _trackerList->len) ) { - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(_buttons[i]), TRUE); - } -} - -void ColorNotebook::_buttonClicked(GtkWidget *widget, SPColorNotebook *colorbook) -{ - ColorNotebook* nb = dynamic_cast<ColorNotebook*>(SP_COLOR_SELECTOR(colorbook)->base); - - if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(widget))) { - return; - } - - for(gint i = 0; i < gtk_notebook_get_n_pages (GTK_NOTEBOOK (nb->_book)); i++) { - if (nb->_buttons[i] == widget) { - gtk_notebook_set_current_page (GTK_NOTEBOOK (nb->_book), i); - } - } -} - -void ColorNotebook::_entryGrabbed (SPColorSelector *, SPColorNotebook *colorbook) -{ - ColorNotebook* nb = dynamic_cast<ColorNotebook*>(SP_COLOR_SELECTOR(colorbook)->base); - nb->_grabbed(); -} - -void ColorNotebook::_entryDragged (SPColorSelector *csel, SPColorNotebook *colorbook) -{ - gboolean oldState; - ColorNotebook* nb = dynamic_cast<ColorNotebook*>(SP_COLOR_SELECTOR(colorbook)->base); - - oldState = nb->_dragging; - - nb->_dragging = TRUE; - nb->_entryModified( csel, colorbook ); - - nb->_dragging = oldState; -} - -void ColorNotebook::_entryReleased (SPColorSelector *, SPColorNotebook *colorbook) -{ - ColorNotebook* nb = dynamic_cast<ColorNotebook*>(SP_COLOR_SELECTOR(colorbook)->base); - nb->_released(); -} - -void ColorNotebook::_entryChanged (SPColorSelector *csel, SPColorNotebook *colorbook) -{ - gboolean oldState; - ColorNotebook* nb = dynamic_cast<ColorNotebook*>(SP_COLOR_SELECTOR(colorbook)->base); - - oldState = nb->_dragging; - - nb->_dragging = FALSE; - nb->_entryModified( csel, colorbook ); - - nb->_dragging = oldState; -} - -void ColorNotebook::_entryModified (SPColorSelector *csel, SPColorNotebook *colorbook) -{ - g_return_if_fail (colorbook != NULL); - g_return_if_fail (SP_IS_COLOR_NOTEBOOK (colorbook)); - g_return_if_fail (csel != NULL); - g_return_if_fail (SP_IS_COLOR_SELECTOR (csel)); - - ColorNotebook* nb = dynamic_cast<ColorNotebook*>(SP_COLOR_SELECTOR(colorbook)->base); - SPColor color; - gfloat alpha = 1.0; - - csel->base->getColorAlpha( color, alpha ); - nb->_updateRgbaEntry( color, alpha ); - nb->_updateInternals( color, alpha, nb->_dragging ); -} - -GtkWidget* ColorNotebook::addPage(GType page_type, guint submode) -{ - GtkWidget *page; - - page = sp_color_selector_new( page_type ); - if ( page ) - { - GtkWidget* tab_label = 0; - SPColorSelector* csel; - - csel = SP_COLOR_SELECTOR (page); - if ( submode > 0 ) - { - csel->base->setSubmode( submode ); - } - gtk_widget_show (page); - int index = csel->base ? csel->base->getSubmode() : 0; - const gchar* str = _(SP_COLOR_SELECTOR_GET_CLASS (csel)->name[index]); -// g_message( "Hitting up for tab for '%s'", str ); - tab_label = gtk_label_new(_(str)); - gint pageNum = gtk_notebook_append_page( GTK_NOTEBOOK (_book), page, tab_label ); - - // Add a button for each page - _buttons[pageNum] = gtk_radio_button_new_with_label(NULL, _(str)); - gtk_toggle_button_set_mode(GTK_TOGGLE_BUTTON(_buttons[pageNum]), FALSE); - if (pageNum > 0) { - GSList *group = gtk_radio_button_get_group (GTK_RADIO_BUTTON(_buttons[0])); - gtk_radio_button_set_group (GTK_RADIO_BUTTON(_buttons[pageNum]), group); - } - gtk_widget_show (_buttons[pageNum]); - gtk_box_pack_start (GTK_BOX (_buttonbox), _buttons[pageNum], TRUE, TRUE, 0); - - g_signal_connect (G_OBJECT (_buttons[pageNum]), "clicked", G_CALLBACK (_buttonClicked), _csel); - g_signal_connect (G_OBJECT (page), "grabbed", G_CALLBACK (_entryGrabbed), _csel); - g_signal_connect (G_OBJECT (page), "dragged", G_CALLBACK (_entryDragged), _csel); - g_signal_connect (G_OBJECT (page), "released", G_CALLBACK (_entryReleased), _csel); - g_signal_connect (G_OBJECT (page), "changed", G_CALLBACK (_entryChanged), _csel); - } - - return page; -} - -GtkWidget* ColorNotebook::getPage(GType page_type, guint submode) -{ - gint count = 0; - gint i = 0; - GtkWidget* page = 0; - -// count = gtk_notebook_get_n_pages (_book); - count = 200; - for ( i = 0; i < count && !page; i++ ) - { - page = gtk_notebook_get_nth_page (GTK_NOTEBOOK (_book), i); - if ( page ) - { - SPColorSelector* csel; - guint pagemode; - csel = SP_COLOR_SELECTOR (page); - pagemode = csel->base->getSubmode(); - if ( G_TYPE_FROM_INSTANCE (page) == page_type - && pagemode == submode ) - { - // found it. - break; - } - else - { - page = 0; - } - } - else - { - break; - } - } - return page; -} - -void ColorNotebook::removePage( GType page_type, guint submode ) -{ - GtkWidget *page = 0; - - page = getPage(page_type, submode); - if ( page ) - { - gint where = gtk_notebook_page_num (GTK_NOTEBOOK (_book), page); - if ( where >= 0 ) - { - if ( gtk_notebook_get_current_page (GTK_NOTEBOOK (_book)) == where ) - { -// getColorAlpha(_color, &_alpha); - } - gtk_notebook_remove_page (GTK_NOTEBOOK (_book), where); - } - } -} - -/* - 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/widgets/sp-color-notebook.h b/src/widgets/sp-color-notebook.h deleted file mode 100644 index 469bb56e8..000000000 --- a/src/widgets/sp-color-notebook.h +++ /dev/null @@ -1,120 +0,0 @@ -#ifndef SEEN_SP_COLOR_NOTEBOOK_H -#define SEEN_SP_COLOR_NOTEBOOK_H - -/* - * A notebook with RGB, CMYK, CMS, HSL, and Wheel pages - * - * Author: - * Lauris Kaplinski <lauris@kaplinski.com> - * - * Copyright (C) 2001-2002 Lauris Kaplinski - * - * This code is in public domain - */ - -#include "sp-color-selector.h" - -#include <glib.h> - -struct SPColorNotebook; - -class ColorNotebook: public ColorSelector -{ -public: - ColorNotebook( SPColorSelector* csel ); - virtual ~ColorNotebook(); - - virtual void init(); - - SPColorSelector* getCurrentSelector(); - void switchPage( GtkNotebook *notebook, GtkWidget *page, guint page_num ); - - GtkWidget* addPage( GType page_type, guint submode ); - void removePage( GType page_type, guint submode ); - GtkWidget* getPage( GType page_type, guint submode ); - - gint menuHandler( GdkEvent* event ); - -protected: - static void _rgbaEntryChangedHook( GtkEntry* entry, SPColorNotebook *colorbook ); - static void _entryGrabbed( SPColorSelector *csel, SPColorNotebook *colorbook ); - static void _entryDragged( SPColorSelector *csel, SPColorNotebook *colorbook ); - static void _entryReleased( SPColorSelector *csel, SPColorNotebook *colorbook ); - static void _entryChanged( SPColorSelector *csel, SPColorNotebook *colorbook ); - static void _entryModified( SPColorSelector *csel, SPColorNotebook *colorbook ); - static void _buttonClicked(GtkWidget *widget, SPColorNotebook *colorbook); - static void _picker_clicked(GtkWidget *widget, SPColorNotebook *colorbook); - - virtual void _colorChanged(); - - void _rgbaEntryChanged( GtkEntry* entry ); - void _updateRgbaEntry( const SPColor& color, gfloat alpha ); - void _setCurrentPage(int i); - - gboolean _updating : 1; - gboolean _updatingrgba : 1; - gboolean _dragging : 1; - gulong _switchId; - gulong _entryId; - GtkWidget *_book; - GtkWidget *_buttonbox; - GtkWidget **_buttons; - GtkWidget *_rgbal, *_rgbae; /* RGBA entry */ -#if defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) - GtkWidget *_box_outofgamut, *_box_colormanaged, *_box_toomuchink; -#endif //defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) - GtkWidget *_btn_picker; - GtkWidget *_p; /* Color preview */ - GtkWidget *_btn; - GtkWidget *_popup; - GPtrArray *_trackerList; - -private: - // By default, disallow copy constructor and assignment operator - ColorNotebook( const ColorNotebook& obj ); - ColorNotebook& operator=( const ColorNotebook& obj ); -}; - - - -#define SP_TYPE_COLOR_NOTEBOOK (sp_color_notebook_get_type ()) -#define SP_COLOR_NOTEBOOK(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), SP_TYPE_COLOR_NOTEBOOK, SPColorNotebook)) -#define SP_COLOR_NOTEBOOK_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), SP_TYPE_COLOR_NOTEBOOK, SPColorNotebookClass)) -#define SP_IS_COLOR_NOTEBOOK(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), SP_TYPE_COLOR_NOTEBOOK)) -#define SP_IS_COLOR_NOTEBOOK_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), SP_TYPE_COLOR_NOTEBOOK)) - -struct SPColorNotebook { - SPColorSelector parent; /* Parent */ -}; - -struct SPColorNotebookClass { - SPColorSelectorClass parent_class; - - void (* grabbed) (SPColorNotebook *rgbsel); - void (* dragged) (SPColorNotebook *rgbsel); - void (* released) (SPColorNotebook *rgbsel); - void (* changed) (SPColorNotebook *rgbsel); -}; - -GType sp_color_notebook_get_type(void); - -GtkWidget *sp_color_notebook_new (void); - -/* void sp_color_notebook_set_mode (SPColorNotebook *csel, SPColorNotebookMode mode); */ -/* SPColorNotebookMode sp_color_notebook_get_mode (SPColorNotebook *csel); */ - - - -#endif // SEEN_SP_COLOR_NOTEBOOK_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/widgets/sp-color-scales.cpp b/src/widgets/sp-color-scales.cpp deleted file mode 100644 index 60ba62ec5..000000000 --- a/src/widgets/sp-color-scales.cpp +++ /dev/null @@ -1,760 +0,0 @@ -/* - * bulia byak <buliabyak@users.sf.net> - */ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif -#include <math.h> -#include <gtk/gtk.h> -#include <glibmm/i18n.h> -#include "ui/dialog-events.h" -#include "sp-color-scales.h" -#include "sp-color-slider.h" -#include "svg/svg-icc-color.h" - -#define CSC_CHANNEL_R (1 << 0) -#define CSC_CHANNEL_G (1 << 1) -#define CSC_CHANNEL_B (1 << 2) -#define CSC_CHANNEL_A (1 << 3) -#define CSC_CHANNEL_H (1 << 0) -#define CSC_CHANNEL_S (1 << 1) -#define CSC_CHANNEL_V (1 << 2) -#define CSC_CHANNEL_C (1 << 0) -#define CSC_CHANNEL_M (1 << 1) -#define CSC_CHANNEL_Y (1 << 2) -#define CSC_CHANNEL_K (1 << 3) -#define CSC_CHANNEL_CMYKA (1 << 4) - -#define CSC_CHANNELS_ALL 0 - - -G_BEGIN_DECLS - -static void sp_color_scales_dispose(GObject *object); - -static void sp_color_scales_show_all (GtkWidget *widget); -static void sp_color_scales_hide(GtkWidget *widget); - -static const gchar *sp_color_scales_hue_map (void); - -G_END_DECLS - -#define XPAD 4 -#define YPAD 1 - -#define noDUMP_CHANGE_INFO 1 - -G_DEFINE_TYPE(SPColorScales, sp_color_scales, SP_TYPE_COLOR_SELECTOR); - -static void -sp_color_scales_class_init (SPColorScalesClass *klass) -{ - static const gchar* nameset[] = {N_("RGB"), N_("HSL"), N_("CMYK"), 0}; - GObjectClass *object_class = G_OBJECT_CLASS(klass); - GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass); - SPColorSelectorClass *selector_class = SP_COLOR_SELECTOR_CLASS (klass); - - selector_class->name = nameset; - selector_class->submode_count = 3; - - object_class->dispose = sp_color_scales_dispose; - - widget_class->show_all = sp_color_scales_show_all; - widget_class->hide = sp_color_scales_hide; -} - -ColorScales::ColorScales( SPColorSelector* csel ) - : ColorSelector( csel ), - _mode( SP_COLOR_SCALES_MODE_NONE ), - _rangeLimit( 255.0 ), - _updating( FALSE ), - _dragging( FALSE ) -{ - for (gint i = 0; i < 5; i++) { - _l[i] = 0; - _a[i] = 0; - _s[i] = 0; - _b[i] = 0; - } -} - -ColorScales::~ColorScales() -{ - for (gint i = 0; i < 5; i++) { - _l[i] = 0; - _a[i] = 0; - _s[i] = 0; - _b[i] = 0; - } -} - -void sp_color_scales_init (SPColorScales *cs) -{ - SP_COLOR_SELECTOR(cs)->base = new ColorScales( SP_COLOR_SELECTOR(cs) ); - - if ( SP_COLOR_SELECTOR(cs)->base ) - { - SP_COLOR_SELECTOR(cs)->base->init(); - } -} - -void ColorScales::init() -{ - gint i; - - _updating = FALSE; - _dragging = FALSE; - -#if GTK_CHECK_VERSION(3,0,0) - GtkWidget *t = gtk_grid_new(); -#else - GtkWidget *t = gtk_table_new (5, 3, FALSE); -#endif - gtk_widget_show (t); - gtk_box_pack_start (GTK_BOX (_csel), t, TRUE, TRUE, 4); - - /* Create components */ - for (i = 0; i < static_cast< gint > (G_N_ELEMENTS(_a)) ; i++) { - /* Label */ - _l[i] = gtk_label_new(""); - gtk_misc_set_alignment (GTK_MISC (_l[i]), 1.0, 0.5); - gtk_widget_show (_l[i]); - -#if GTK_CHECK_VERSION(3,0,0) - #if GTK_CHECK_VERSION(3,12,0) - gtk_widget_set_margin_start(_l[i], XPAD); - gtk_widget_set_margin_end(_l[i], XPAD); - #else - gtk_widget_set_margin_left(_l[i], XPAD); - gtk_widget_set_margin_right(_l[i], XPAD); - #endif - gtk_widget_set_margin_top(_l[i], YPAD); - gtk_widget_set_margin_bottom(_l[i], YPAD); - gtk_grid_attach(GTK_GRID(t), _l[i], 0, i, 1, 1); -#else - gtk_table_attach (GTK_TABLE (t), _l[i], 0, 1, i, i + 1, GTK_FILL, GTK_FILL, XPAD, YPAD); -#endif - - /* Adjustment */ - _a[i] = GTK_ADJUSTMENT(gtk_adjustment_new(0.0, 0.0, _rangeLimit, 1.0, 10.0, 10.0)); - /* Slider */ - _s[i] = sp_color_slider_new (_a[i]); - gtk_widget_show (_s[i]); - -#if GTK_CHECK_VERSION(3,0,0) - #if GTK_CHECK_VERSION(3,12,0) - gtk_widget_set_margin_start(_s[i], XPAD); - gtk_widget_set_margin_end(_s[i], XPAD); - #else - gtk_widget_set_margin_left(_s[i], XPAD); - gtk_widget_set_margin_right(_s[i], XPAD); - #endif - gtk_widget_set_margin_top(_s[i], YPAD); - gtk_widget_set_margin_bottom(_s[i], YPAD); - gtk_widget_set_hexpand(_s[i], TRUE); - gtk_grid_attach(GTK_GRID(t), _s[i], 1, i, 1, 1); -#else - gtk_table_attach (GTK_TABLE (t), _s[i], 1, 2, i, i + 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), GTK_FILL, XPAD, YPAD); -#endif - - /* Spinbutton */ - _b[i] = gtk_spin_button_new (GTK_ADJUSTMENT (_a[i]), 1.0, 0); - sp_dialog_defocus_on_enter (_b[i]); - gtk_label_set_mnemonic_widget (GTK_LABEL(_l[i]), _b[i]); - gtk_widget_show (_b[i]); - -#if GTK_CHECK_VERSION(3,0,0) - #if GTK_CHECK_VERSION(3,12,0) - gtk_widget_set_margin_start(_b[i], XPAD); - gtk_widget_set_margin_end(_b[i], XPAD); - #else - gtk_widget_set_margin_left(_b[i], XPAD); - gtk_widget_set_margin_right(_b[i], XPAD); - #endif - gtk_widget_set_margin_top(_b[i], YPAD); - gtk_widget_set_margin_bottom(_b[i], YPAD); - gtk_widget_set_halign(_b[i], GTK_ALIGN_CENTER); - gtk_widget_set_valign(_b[i], GTK_ALIGN_CENTER); - gtk_grid_attach(GTK_GRID(t), _b[i], 2, i, 1, 1); -#else - gtk_table_attach (GTK_TABLE (t), _b[i], 2, 3, i, i + 1, (GtkAttachOptions)0, (GtkAttachOptions)0, XPAD, YPAD); -#endif - - /* Attach channel value to adjustment */ - g_object_set_data (G_OBJECT (_a[i]), "channel", GINT_TO_POINTER (i)); - /* Signals */ - g_signal_connect (G_OBJECT (_a[i]), "value_changed", - G_CALLBACK (_adjustmentAnyChanged), _csel); - g_signal_connect (G_OBJECT (_s[i]), "grabbed", - G_CALLBACK (_sliderAnyGrabbed), _csel); - g_signal_connect (G_OBJECT (_s[i]), "released", - G_CALLBACK (_sliderAnyReleased), _csel); - g_signal_connect (G_OBJECT (_s[i]), "changed", - G_CALLBACK (_sliderAnyChanged), _csel); - } - - /* Initial mode is none, so it works */ - setMode(SP_COLOR_SCALES_MODE_RGB); -} - -static void sp_color_scales_dispose(GObject *object) -{ - if (G_OBJECT_CLASS(sp_color_scales_parent_class)->dispose) - G_OBJECT_CLASS(sp_color_scales_parent_class)->dispose(object); -} - -static void -sp_color_scales_show_all (GtkWidget *widget) -{ - gtk_widget_show (widget); -} - -static void sp_color_scales_hide(GtkWidget *widget) -{ - gtk_widget_hide(widget); -} - -GtkWidget *sp_color_scales_new() -{ - SPColorScales *csel = SP_COLOR_SCALES(g_object_new (SP_TYPE_COLOR_SCALES, NULL)); - - return GTK_WIDGET (csel); -} - -void ColorScales::_recalcColor( gboolean changing ) -{ - if ( changing ) - { - SPColor color; - gfloat alpha = 1.0; - gfloat c[5]; - - switch (_mode) { - case SP_COLOR_SCALES_MODE_RGB: - case SP_COLOR_SCALES_MODE_HSV: - _getRgbaFloatv(c); - color.set( c[0], c[1], c[2] ); - alpha = c[3]; - break; - case SP_COLOR_SCALES_MODE_CMYK: - { - _getCmykaFloatv( c ); - - float rgb[3]; - sp_color_cmyk_to_rgb_floatv( rgb, c[0], c[1], c[2], c[3] ); - color.set( rgb[0], rgb[1], rgb[2] ); - alpha = c[4]; - break; - } - default: - g_warning ("file %s: line %d: Illegal color selector mode %d", __FILE__, __LINE__, _mode); - break; - } - - /* Preserve ICC */ - color.icc = _color.icc ? new SVGICCColor(*_color.icc) : 0; - - _updateInternals( color, alpha, _dragging ); - } - else - { - _updateInternals( _color, _alpha, _dragging ); - } -} - -/* Helpers for setting color value */ -gfloat ColorScales::getScaled( const GtkAdjustment *a ) -{ - gfloat val = gtk_adjustment_get_value (const_cast<GtkAdjustment*>(a)) - / gtk_adjustment_get_upper (const_cast<GtkAdjustment*>(a)); - return val; -} - -void ColorScales::setScaled( GtkAdjustment *a, gfloat v ) -{ - gfloat val = v * gtk_adjustment_get_upper (a); - gtk_adjustment_set_value( a, val ); -} - -void ColorScales::_setRangeLimit( gdouble upper ) -{ - _rangeLimit = upper; - for ( gint i = 0; i < static_cast<gint>(G_N_ELEMENTS(_a)); i++ ) { - gtk_adjustment_set_upper (_a[i], upper); - gtk_adjustment_changed( _a[i] ); - } -} - -void ColorScales::_colorChanged() -{ -#ifdef DUMP_CHANGE_INFO - g_message("ColorScales::_colorChanged( this=%p, %f, %f, %f, %f)", this, _color.v.c[0], _color.v.c[1], _color.v.c[2], _alpha ); -#endif - gfloat tmp[3]; - gfloat c[5] = {0.0, 0.0, 0.0, 0.0}; - - switch (_mode) { - case SP_COLOR_SCALES_MODE_RGB: - sp_color_get_rgb_floatv( &_color, c ); - c[3] = _alpha; - c[4] = 0.0; - break; - case SP_COLOR_SCALES_MODE_HSV: - sp_color_get_rgb_floatv( &_color, tmp ); - sp_color_rgb_to_hsl_floatv (c, tmp[0], tmp[1], tmp[2]); - c[3] = _alpha; - c[4] = 0.0; - break; - case SP_COLOR_SCALES_MODE_CMYK: - sp_color_get_cmyk_floatv( &_color, c ); - c[4] = _alpha; - break; - default: - g_warning ("file %s: line %d: Illegal color selector mode %d", __FILE__, __LINE__, _mode); - break; - } - - _updating = TRUE; - setScaled( _a[0], c[0] ); - setScaled( _a[1], c[1] ); - setScaled( _a[2], c[2] ); - setScaled( _a[3], c[3] ); - setScaled( _a[4], c[4] ); - _updateSliders( CSC_CHANNELS_ALL ); - _updating = FALSE; -} - -void ColorScales::_getRgbaFloatv( gfloat *rgba ) -{ - g_return_if_fail (rgba != NULL); - - switch (_mode) { - case SP_COLOR_SCALES_MODE_RGB: - rgba[0] = getScaled(_a[0]); - rgba[1] = getScaled(_a[1]); - rgba[2] = getScaled(_a[2]); - rgba[3] = getScaled(_a[3]); - break; - case SP_COLOR_SCALES_MODE_HSV: - sp_color_hsl_to_rgb_floatv (rgba, getScaled(_a[0]), getScaled(_a[1]), getScaled(_a[2])); - rgba[3] = getScaled(_a[3]); - break; - case SP_COLOR_SCALES_MODE_CMYK: - sp_color_cmyk_to_rgb_floatv (rgba, getScaled(_a[0]), getScaled(_a[1]), getScaled(_a[2]), getScaled(_a[3])); - rgba[3] = getScaled(_a[4]); - break; - default: - g_warning ("file %s: line %d: Illegal color selector mode", __FILE__, __LINE__); - break; - } -} - -void ColorScales::_getCmykaFloatv( gfloat *cmyka ) -{ - gfloat rgb[3]; - - g_return_if_fail (cmyka != NULL); - - switch (_mode) { - case SP_COLOR_SCALES_MODE_RGB: - sp_color_rgb_to_cmyk_floatv (cmyka, getScaled(_a[0]), getScaled(_a[1]), getScaled(_a[2])); - cmyka[4] = getScaled(_a[3]); - break; - case SP_COLOR_SCALES_MODE_HSV: - sp_color_hsl_to_rgb_floatv (rgb, getScaled(_a[0]), getScaled(_a[1]), getScaled(_a[2])); - sp_color_rgb_to_cmyk_floatv (cmyka, rgb[0], rgb[1], rgb[2]); - cmyka[4] = getScaled(_a[3]); - break; - case SP_COLOR_SCALES_MODE_CMYK: - cmyka[0] = getScaled(_a[0]); - cmyka[1] = getScaled(_a[1]); - cmyka[2] = getScaled(_a[2]); - cmyka[3] = getScaled(_a[3]); - cmyka[4] = getScaled(_a[4]); - break; - default: - g_warning ("file %s: line %d: Illegal color selector mode", __FILE__, __LINE__); - break; - } -} - -guint32 ColorScales::_getRgba32() -{ - gfloat c[4]; - guint32 rgba; - - _getRgbaFloatv(c); - - rgba = SP_RGBA32_F_COMPOSE (c[0], c[1], c[2], c[3]); - - return rgba; -} - -void ColorScales::setMode(SPColorScalesMode mode) -{ - gfloat rgba[4]; - gfloat c[4]; - - if (_mode == mode) return; - - if ((_mode == SP_COLOR_SCALES_MODE_RGB) || - (_mode == SP_COLOR_SCALES_MODE_HSV) || - (_mode == SP_COLOR_SCALES_MODE_CMYK)) { - _getRgbaFloatv(rgba); - } else { - rgba[0] = rgba[1] = rgba[2] = rgba[3] = 1.0; - } - - _mode = mode; - - switch (mode) { - case SP_COLOR_SCALES_MODE_RGB: - _setRangeLimit(255.0); - gtk_label_set_markup_with_mnemonic (GTK_LABEL (_l[0]), _("_R:")); - gtk_widget_set_tooltip_text (_s[0], _("Red")); - gtk_widget_set_tooltip_text (_b[0], _("Red")); - gtk_label_set_markup_with_mnemonic (GTK_LABEL (_l[1]), _("_G:")); - gtk_widget_set_tooltip_text (_s[1], _("Green")); - gtk_widget_set_tooltip_text (_b[1], _("Green")); - gtk_label_set_markup_with_mnemonic (GTK_LABEL (_l[2]), _("_B:")); - gtk_widget_set_tooltip_text (_s[2], _("Blue")); - gtk_widget_set_tooltip_text (_b[2], _("Blue")); - gtk_label_set_markup_with_mnemonic (GTK_LABEL (_l[3]), _("_A:")); - gtk_widget_set_tooltip_text (_s[3], _("Alpha (opacity)")); - gtk_widget_set_tooltip_text (_b[3], _("Alpha (opacity)")); - sp_color_slider_set_map (SP_COLOR_SLIDER (_s[0]), NULL); - gtk_widget_hide (_l[4]); - gtk_widget_hide (_s[4]); - gtk_widget_hide (_b[4]); - _updating = TRUE; - setScaled( _a[0], rgba[0] ); - setScaled( _a[1], rgba[1] ); - setScaled( _a[2], rgba[2] ); - setScaled( _a[3], rgba[3] ); - _updating = FALSE; - _updateSliders( CSC_CHANNELS_ALL ); - break; - case SP_COLOR_SCALES_MODE_HSV: - _setRangeLimit(255.0); - gtk_label_set_markup_with_mnemonic (GTK_LABEL (_l[0]), _("_H:")); - gtk_widget_set_tooltip_text (_s[0], _("Hue")); - gtk_widget_set_tooltip_text (_b[0], _("Hue")); - gtk_label_set_markup_with_mnemonic (GTK_LABEL (_l[1]), _("_S:")); - gtk_widget_set_tooltip_text (_s[1], _("Saturation")); - gtk_widget_set_tooltip_text (_b[1], _("Saturation")); - gtk_label_set_markup_with_mnemonic (GTK_LABEL (_l[2]), _("_L:")); - gtk_widget_set_tooltip_text (_s[2], _("Lightness")); - gtk_widget_set_tooltip_text (_b[2], _("Lightness")); - gtk_label_set_markup_with_mnemonic (GTK_LABEL (_l[3]), _("_A:")); - gtk_widget_set_tooltip_text (_s[3], _("Alpha (opacity)")); - gtk_widget_set_tooltip_text (_b[3], _("Alpha (opacity)")); - sp_color_slider_set_map (SP_COLOR_SLIDER (_s[0]), (guchar *)(sp_color_scales_hue_map())); - gtk_widget_hide (_l[4]); - gtk_widget_hide (_s[4]); - gtk_widget_hide (_b[4]); - _updating = TRUE; - c[0] = 0.0; - sp_color_rgb_to_hsl_floatv (c, rgba[0], rgba[1], rgba[2]); - setScaled( _a[0], c[0] ); - setScaled( _a[1], c[1] ); - setScaled( _a[2], c[2] ); - setScaled( _a[3], rgba[3] ); - _updating = FALSE; - _updateSliders( CSC_CHANNELS_ALL ); - break; - case SP_COLOR_SCALES_MODE_CMYK: - _setRangeLimit(100.0); - gtk_label_set_markup_with_mnemonic (GTK_LABEL (_l[0]), _("_C:")); - gtk_widget_set_tooltip_text (_s[0], _("Cyan")); - gtk_widget_set_tooltip_text (_b[0], _("Cyan")); - gtk_label_set_markup_with_mnemonic (GTK_LABEL (_l[1]), _("_M:")); - gtk_widget_set_tooltip_text (_s[1], _("Magenta")); - gtk_widget_set_tooltip_text (_b[1], _("Magenta")); - gtk_label_set_markup_with_mnemonic (GTK_LABEL (_l[2]), _("_Y:")); - gtk_widget_set_tooltip_text (_s[2], _("Yellow")); - gtk_widget_set_tooltip_text (_b[2], _("Yellow")); - gtk_label_set_markup_with_mnemonic (GTK_LABEL (_l[3]), _("_K:")); - gtk_widget_set_tooltip_text (_s[3], _("Black")); - gtk_widget_set_tooltip_text (_b[3], _("Black")); - gtk_label_set_markup_with_mnemonic (GTK_LABEL (_l[4]), _("_A:")); - gtk_widget_set_tooltip_text (_s[4], _("Alpha (opacity)")); - gtk_widget_set_tooltip_text (_b[4], _("Alpha (opacity)")); - sp_color_slider_set_map (SP_COLOR_SLIDER (_s[0]), NULL); - gtk_widget_show (_l[4]); - gtk_widget_show (_s[4]); - gtk_widget_show (_b[4]); - _updating = TRUE; - - sp_color_rgb_to_cmyk_floatv (c, rgba[0], rgba[1], rgba[2]); - setScaled( _a[0], c[0] ); - setScaled( _a[1], c[1] ); - setScaled( _a[2], c[2] ); - setScaled( _a[3], c[3] ); - - setScaled( _a[4], rgba[3] ); - _updating = FALSE; - _updateSliders( CSC_CHANNELS_ALL ); - break; - default: - g_warning ("file %s: line %d: Illegal color selector mode", __FILE__, __LINE__); - break; - } -} - -SPColorScalesMode ColorScales::getMode() const -{ - return _mode; -} - -void ColorScales::setSubmode( guint submode ) -{ - g_return_if_fail (_csel != NULL); - g_return_if_fail (SP_IS_COLOR_SCALES (_csel)); - g_return_if_fail (submode < 3); - - switch ( submode ) - { - default: - case 0: - setMode(SP_COLOR_SCALES_MODE_RGB); - break; - case 1: - setMode(SP_COLOR_SCALES_MODE_HSV); - break; - case 2: - setMode(SP_COLOR_SCALES_MODE_CMYK); - break; - } -} - -guint ColorScales::getSubmode() const -{ - guint submode = 0; - - switch ( _mode ) - { - case SP_COLOR_SCALES_MODE_HSV: - submode = 1; - break; - case SP_COLOR_SCALES_MODE_CMYK: - submode = 2; - break; - case SP_COLOR_SCALES_MODE_RGB: - default: - submode = 0; - } - - return submode; -} - -void ColorScales::_adjustmentAnyChanged( GtkAdjustment *adjustment, SPColorScales *cs ) -{ - gint channel = GPOINTER_TO_INT (g_object_get_data(G_OBJECT (adjustment), "channel")); - - _adjustmentChanged(cs, channel); -} - -void ColorScales::_sliderAnyGrabbed( SPColorSlider *slider, SPColorScales *cs ) -{ - (void)slider; - ColorScales* scales = static_cast<ColorScales*>(SP_COLOR_SELECTOR(cs)->base); - if (!scales->_dragging) { - scales->_dragging = TRUE; - scales->_grabbed(); - scales->_recalcColor( FALSE ); - } -} - -void ColorScales::_sliderAnyReleased( SPColorSlider *slider, SPColorScales *cs ) -{ - (void)slider; - ColorScales* scales = static_cast<ColorScales*>(SP_COLOR_SELECTOR(cs)->base); - if (scales->_dragging) { - scales->_dragging = FALSE; - scales->_released(); - scales->_recalcColor( FALSE ); - } -} - -void ColorScales::_sliderAnyChanged( SPColorSlider *slider, SPColorScales *cs ) -{ - (void)slider; - ColorScales* scales = static_cast<ColorScales*>(SP_COLOR_SELECTOR(cs)->base); - - scales->_recalcColor( TRUE ); -} - -void ColorScales::_adjustmentChanged( SPColorScales *cs, guint channel ) -{ - ColorScales* scales = static_cast<ColorScales*>(SP_COLOR_SELECTOR(cs)->base); - if (scales->_updating) return; - - scales->_updating = TRUE; - - scales->_updateSliders( (1 << channel) ); - - scales->_recalcColor (TRUE); - - scales->_updating = FALSE; -} - -void ColorScales::_updateSliders( guint channels ) -{ - gfloat rgb0[3], rgbm[3], rgb1[3]; -#ifdef SPCS_PREVIEW - guint32 rgba; -#endif - switch (_mode) { - case SP_COLOR_SCALES_MODE_RGB: - if ((channels != CSC_CHANNEL_R) && (channels != CSC_CHANNEL_A)) { - /* Update red */ - sp_color_slider_set_colors (SP_COLOR_SLIDER (_s[0]), - SP_RGBA32_F_COMPOSE (0.0, getScaled(_a[1]), getScaled(_a[2]), 1.0), - SP_RGBA32_F_COMPOSE (0.5, getScaled(_a[1]), getScaled(_a[2]), 1.0), - SP_RGBA32_F_COMPOSE (1.0, getScaled(_a[1]), getScaled(_a[2]), 1.0)); - } - if ((channels != CSC_CHANNEL_G) && (channels != CSC_CHANNEL_A)) { - /* Update green */ - sp_color_slider_set_colors (SP_COLOR_SLIDER (_s[1]), - SP_RGBA32_F_COMPOSE (getScaled(_a[0]), 0.0, getScaled(_a[2]), 1.0), - SP_RGBA32_F_COMPOSE (getScaled(_a[0]), 0.5, getScaled(_a[2]), 1.0), - SP_RGBA32_F_COMPOSE (getScaled(_a[0]), 1.0, getScaled(_a[2]), 1.0)); - } - if ((channels != CSC_CHANNEL_B) && (channels != CSC_CHANNEL_A)) { - /* Update blue */ - sp_color_slider_set_colors (SP_COLOR_SLIDER (_s[2]), - SP_RGBA32_F_COMPOSE (getScaled(_a[0]), getScaled(_a[1]), 0.0, 1.0), - SP_RGBA32_F_COMPOSE (getScaled(_a[0]), getScaled(_a[1]), 0.5, 1.0), - SP_RGBA32_F_COMPOSE (getScaled(_a[0]), getScaled(_a[1]), 1.0, 1.0)); - } - if (channels != CSC_CHANNEL_A) { - /* Update alpha */ - sp_color_slider_set_colors (SP_COLOR_SLIDER (_s[3]), - SP_RGBA32_F_COMPOSE (getScaled(_a[0]), getScaled(_a[1]), getScaled(_a[2]), 0.0), - SP_RGBA32_F_COMPOSE (getScaled(_a[0]), getScaled(_a[1]), getScaled(_a[2]), 0.5), - SP_RGBA32_F_COMPOSE (getScaled(_a[0]), getScaled(_a[1]), getScaled(_a[2]), 1.0)); - } - break; - case SP_COLOR_SCALES_MODE_HSV: - /* Hue is never updated */ - if ((channels != CSC_CHANNEL_S) && (channels != CSC_CHANNEL_A)) { - /* Update saturation */ - sp_color_hsl_to_rgb_floatv (rgb0, getScaled(_a[0]), 0.0, getScaled(_a[2])); - sp_color_hsl_to_rgb_floatv (rgbm, getScaled(_a[0]), 0.5, getScaled(_a[2])); - sp_color_hsl_to_rgb_floatv (rgb1, getScaled(_a[0]), 1.0, getScaled(_a[2])); - sp_color_slider_set_colors (SP_COLOR_SLIDER (_s[1]), - SP_RGBA32_F_COMPOSE (rgb0[0], rgb0[1], rgb0[2], 1.0), - SP_RGBA32_F_COMPOSE (rgbm[0], rgbm[1], rgbm[2], 1.0), - SP_RGBA32_F_COMPOSE (rgb1[0], rgb1[1], rgb1[2], 1.0)); - } - if ((channels != CSC_CHANNEL_V) && (channels != CSC_CHANNEL_A)) { - /* Update value */ - sp_color_hsl_to_rgb_floatv (rgb0, getScaled(_a[0]), getScaled(_a[1]), 0.0); - sp_color_hsl_to_rgb_floatv (rgbm, getScaled(_a[0]), getScaled(_a[1]), 0.5); - sp_color_hsl_to_rgb_floatv (rgb1, getScaled(_a[0]), getScaled(_a[1]), 1.0); - sp_color_slider_set_colors (SP_COLOR_SLIDER (_s[2]), - SP_RGBA32_F_COMPOSE (rgb0[0], rgb0[1], rgb0[2], 1.0), - SP_RGBA32_F_COMPOSE (rgbm[0], rgbm[1], rgbm[2], 1.0), - SP_RGBA32_F_COMPOSE (rgb1[0], rgb1[1], rgb1[2], 1.0)); - } - if (channels != CSC_CHANNEL_A) { - /* Update alpha */ - sp_color_hsl_to_rgb_floatv (rgb0, getScaled(_a[0]), getScaled(_a[1]), getScaled(_a[2])); - sp_color_slider_set_colors (SP_COLOR_SLIDER (_s[3]), - SP_RGBA32_F_COMPOSE (rgb0[0], rgb0[1], rgb0[2], 0.0), - SP_RGBA32_F_COMPOSE (rgb0[0], rgb0[1], rgb0[2], 0.5), - SP_RGBA32_F_COMPOSE (rgb0[0], rgb0[1], rgb0[2], 1.0)); - } - break; - case SP_COLOR_SCALES_MODE_CMYK: - if ((channels != CSC_CHANNEL_C) && (channels != CSC_CHANNEL_CMYKA)) { - /* Update C */ - sp_color_cmyk_to_rgb_floatv (rgb0, 0.0, getScaled(_a[1]), getScaled(_a[2]), getScaled(_a[3])); - sp_color_cmyk_to_rgb_floatv (rgbm, 0.5, getScaled(_a[1]), getScaled(_a[2]), getScaled(_a[3])); - sp_color_cmyk_to_rgb_floatv (rgb1, 1.0, getScaled(_a[1]), getScaled(_a[2]), getScaled(_a[3])); - sp_color_slider_set_colors (SP_COLOR_SLIDER (_s[0]), - SP_RGBA32_F_COMPOSE (rgb0[0], rgb0[1], rgb0[2], 1.0), - SP_RGBA32_F_COMPOSE (rgbm[0], rgbm[1], rgbm[2], 1.0), - SP_RGBA32_F_COMPOSE (rgb1[0], rgb1[1], rgb1[2], 1.0)); - } - if ((channels != CSC_CHANNEL_M) && (channels != CSC_CHANNEL_CMYKA)) { - /* Update M */ - sp_color_cmyk_to_rgb_floatv (rgb0, getScaled(_a[0]), 0.0, getScaled(_a[2]), getScaled(_a[3])); - sp_color_cmyk_to_rgb_floatv (rgbm, getScaled(_a[0]), 0.5, getScaled(_a[2]), getScaled(_a[3])); - sp_color_cmyk_to_rgb_floatv (rgb1, getScaled(_a[0]), 1.0, getScaled(_a[2]), getScaled(_a[3])); - sp_color_slider_set_colors (SP_COLOR_SLIDER (_s[1]), - SP_RGBA32_F_COMPOSE (rgb0[0], rgb0[1], rgb0[2], 1.0), - SP_RGBA32_F_COMPOSE (rgbm[0], rgbm[1], rgbm[2], 1.0), - SP_RGBA32_F_COMPOSE (rgb1[0], rgb1[1], rgb1[2], 1.0)); - } - if ((channels != CSC_CHANNEL_Y) && (channels != CSC_CHANNEL_CMYKA)) { - /* Update Y */ - sp_color_cmyk_to_rgb_floatv (rgb0, getScaled(_a[0]), getScaled(_a[1]), 0.0, getScaled(_a[3])); - sp_color_cmyk_to_rgb_floatv (rgbm, getScaled(_a[0]), getScaled(_a[1]), 0.5, getScaled(_a[3])); - sp_color_cmyk_to_rgb_floatv (rgb1, getScaled(_a[0]), getScaled(_a[1]), 1.0, getScaled(_a[3])); - sp_color_slider_set_colors (SP_COLOR_SLIDER (_s[2]), - SP_RGBA32_F_COMPOSE (rgb0[0], rgb0[1], rgb0[2], 1.0), - SP_RGBA32_F_COMPOSE (rgbm[0], rgbm[1], rgbm[2], 1.0), - SP_RGBA32_F_COMPOSE (rgb1[0], rgb1[1], rgb1[2], 1.0)); - } - if ((channels != CSC_CHANNEL_K) && (channels != CSC_CHANNEL_CMYKA)) { - /* Update K */ - sp_color_cmyk_to_rgb_floatv (rgb0, getScaled(_a[0]), getScaled(_a[1]), getScaled(_a[2]), 0.0); - sp_color_cmyk_to_rgb_floatv (rgbm, getScaled(_a[0]), getScaled(_a[1]), getScaled(_a[2]), 0.5); - sp_color_cmyk_to_rgb_floatv (rgb1, getScaled(_a[0]), getScaled(_a[1]), getScaled(_a[2]), 1.0); - sp_color_slider_set_colors (SP_COLOR_SLIDER (_s[3]), - SP_RGBA32_F_COMPOSE (rgb0[0], rgb0[1], rgb0[2], 1.0), - SP_RGBA32_F_COMPOSE (rgbm[0], rgbm[1], rgbm[2], 1.0), - SP_RGBA32_F_COMPOSE (rgb1[0], rgb1[1], rgb1[2], 1.0)); - } - if (channels != CSC_CHANNEL_CMYKA) { - /* Update alpha */ - sp_color_cmyk_to_rgb_floatv (rgb0, getScaled(_a[0]), getScaled(_a[1]), getScaled(_a[2]), getScaled(_a[3])); - sp_color_slider_set_colors (SP_COLOR_SLIDER (_s[4]), - SP_RGBA32_F_COMPOSE (rgb0[0], rgb0[1], rgb0[2], 0.0), - SP_RGBA32_F_COMPOSE (rgb0[0], rgb0[1], rgb0[2], 0.5), - SP_RGBA32_F_COMPOSE (rgb0[0], rgb0[1], rgb0[2], 1.0)); - } - break; - default: - g_warning ("file %s: line %d: Illegal color selector mode", __FILE__, __LINE__); - break; - } - - // Force the internal color to be updated - if ( !_updating ) - { - _recalcColor( TRUE ); - } - -#ifdef SPCS_PREVIEW - rgba = sp_color_scales_get_rgba32 (cs); - sp_color_preview_set_rgba32 (SP_COLOR_PREVIEW (_p), rgba); -#endif -} - -static const gchar * -sp_color_scales_hue_map (void) -{ - static gchar *map = NULL; - - if (!map) { - gchar *p; - gint h; - map = g_new (gchar, 4 * 1024); - p = map; - for (h = 0; h < 1024; h++) { - gfloat rgb[3]; - sp_color_hsl_to_rgb_floatv (rgb, h / 1024.0, 1.0, 0.5); - *p++ = SP_COLOR_F_TO_U (rgb[0]); - *p++ = SP_COLOR_F_TO_U (rgb[1]); - *p++ = SP_COLOR_F_TO_U (rgb[2]); - *p++ = 255; - } - } - - return map; -} - - diff --git a/src/widgets/sp-color-scales.h b/src/widgets/sp-color-scales.h deleted file mode 100644 index 72cbafa2f..000000000 --- a/src/widgets/sp-color-scales.h +++ /dev/null @@ -1,102 +0,0 @@ -#ifndef SEEN_SP_COLOR_SCALES_H -#define SEEN_SP_COLOR_SCALES_H - -#include <glib.h> - -#include <widgets/sp-color-selector.h> - -struct SPColorScales; -struct SPColorScalesClass; -struct SPColorSlider; - -typedef enum { - SP_COLOR_SCALES_MODE_NONE = 0, - SP_COLOR_SCALES_MODE_RGB = 1, - SP_COLOR_SCALES_MODE_HSV = 2, - SP_COLOR_SCALES_MODE_CMYK = 3 -} SPColorScalesMode; - - - -class ColorScales: public ColorSelector -{ -public: - static gfloat getScaled( const GtkAdjustment *a ); - static void setScaled( GtkAdjustment *a, gfloat v); - - ColorScales(SPColorSelector *csel); - virtual ~ColorScales(); - - virtual void init(); - - virtual void setSubmode(guint submode); - virtual guint getSubmode() const; - - void setMode(SPColorScalesMode mode); - SPColorScalesMode getMode() const; - - -protected: - virtual void _colorChanged(); - - static void _adjustmentAnyChanged(GtkAdjustment *adjustment, SPColorScales *cs); - static void _sliderAnyGrabbed(SPColorSlider *slider, SPColorScales *cs); - static void _sliderAnyReleased(SPColorSlider *slider, SPColorScales *cs); - static void _sliderAnyChanged(SPColorSlider *slider, SPColorScales *cs); - static void _adjustmentChanged(SPColorScales *cs, guint channel); - - void _getRgbaFloatv(gfloat *rgba); - void _getCmykaFloatv(gfloat *cmyka); - guint32 _getRgba32(); - void _updateSliders(guint channels); - void _recalcColor(gboolean changing); - - void _setRangeLimit( gdouble upper ); - - SPColorScalesMode _mode; - gdouble _rangeLimit; - gboolean _updating : 1; - gboolean _dragging : 1; - GtkAdjustment *_a[5]; /* Channel adjustments */ - GtkWidget *_s[5]; /* Channel sliders */ - GtkWidget *_b[5]; /* Spinbuttons */ - GtkWidget *_l[5]; /* Labels */ - -private: - // By default, disallow copy constructor and assignment operator - ColorScales(ColorScales const &obj); - ColorScales &operator=(ColorScales const &obj ); -}; - - - -#define SP_TYPE_COLOR_SCALES (sp_color_scales_get_type()) -#define SP_COLOR_SCALES(o) (G_TYPE_CHECK_INSTANCE_CAST((o), SP_TYPE_COLOR_SCALES, SPColorScales)) -#define SP_COLOR_SCALES_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), SP_TYPE_COLOR_SCALES, SPColorScalesClass)) -#define SP_IS_COLOR_SCALES(o) (G_TYPE_CHECK_INSTANCE_TYPE((o), SP_TYPE_COLOR_SCALES)) -#define SP_IS_COLOR_SCALES_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE((k), SP_TYPE_COLOR_SCALES)) - -struct SPColorScales { - SPColorSelector parent; -}; - -struct SPColorScalesClass { - SPColorSelectorClass parent_class; -}; - -GType sp_color_scales_get_type(); - -GtkWidget *sp_color_scales_new(); - -#endif /* !SEEN_SP_COLOR_SCALES_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 : diff --git a/src/widgets/sp-color-selector.cpp b/src/widgets/sp-color-selector.cpp index e97c36431..dad0a18b0 100644 --- a/src/widgets/sp-color-selector.cpp +++ b/src/widgets/sp-color-selector.cpp @@ -6,6 +6,11 @@ #ifdef HAVE_CONFIG_H # include "config.h" #endif + +#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H +#include <glibmm/threads.h> +#endif + #include <math.h> #include <gtk/gtk.h> #include <glibmm/i18n.h> @@ -289,7 +294,7 @@ void ColorSelector::_updateInternals( const SPColor& color, gfloat alpha, gboole (_held ? "CHANGED" : "DRAGGED" ), color.toRGBA32( alpha ), (color.icc?color.icc->colorProfile.c_str():"<null>"), FOO_NAME(_csel)); #endif - g_signal_emit(G_OBJECT(_csel), csel_signals[_held ? CHANGED : DRAGGED], 0); + g_signal_emit(G_OBJECT(_csel), csel_signals[_held ? DRAGGED : CHANGED], 0); } } diff --git a/src/widgets/sp-color-selector.h b/src/widgets/sp-color-selector.h index 30061774a..308a5519c 100644 --- a/src/widgets/sp-color-selector.h +++ b/src/widgets/sp-color-selector.h @@ -1,6 +1,14 @@ #ifndef SEEN_SP_COLOR_SELECTOR_H #define SEEN_SP_COLOR_SELECTOR_H +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H +#include <glibmm/threads.h> +#endif + #include <gtk/gtk.h> #include "color.h" diff --git a/src/widgets/sp-color-slider.cpp b/src/widgets/sp-color-slider.cpp deleted file mode 100644 index ab7e2cd84..000000000 --- a/src/widgets/sp-color-slider.cpp +++ /dev/null @@ -1,749 +0,0 @@ -/* - * A slider with colored background - * - * Author: - * Lauris Kaplinski <lauris@kaplinski.com> - * bulia byak <buliabyak@users.sf.net> - * - * Copyright (C) 2001-2002 Lauris Kaplinski - * - * This code is in public domain - */ - -#include <gtk/gtk.h> -#include "sp-color-scales.h" -#include "sp-color-slider.h" -#include "preferences.h" - -#define SLIDER_WIDTH 96 -#define SLIDER_HEIGHT 8 -#define ARROW_SIZE 7 - -enum { - GRABBED, - DRAGGED, - RELEASED, - CHANGED, - LAST_SIGNAL -}; - -static void sp_color_slider_dispose(GObject *object); - -static void sp_color_slider_realize (GtkWidget *widget); -static void sp_color_slider_size_request (GtkWidget *widget, GtkRequisition *requisition); - -#if GTK_CHECK_VERSION(3,0,0) -static void sp_color_slider_get_preferred_width(GtkWidget *widget, - gint *minimal_width, - gint *natural_width); - -static void sp_color_slider_get_preferred_height(GtkWidget *widget, - gint *minimal_height, - gint *natural_height); -#else -static gboolean sp_color_slider_expose(GtkWidget *widget, GdkEventExpose *event); -#endif - -static void sp_color_slider_size_allocate (GtkWidget *widget, GtkAllocation *allocation); - -static gboolean sp_color_slider_draw(GtkWidget *widget, cairo_t *cr); - -static gint sp_color_slider_button_press (GtkWidget *widget, GdkEventButton *event); -static gint sp_color_slider_button_release (GtkWidget *widget, GdkEventButton *event); -static gint sp_color_slider_motion_notify (GtkWidget *widget, GdkEventMotion *event); - -static void sp_color_slider_adjustment_changed (GtkAdjustment *adjustment, SPColorSlider *slider); -static void sp_color_slider_adjustment_value_changed (GtkAdjustment *adjustment, SPColorSlider *slider); - -static const guchar *sp_color_slider_render_gradient (gint x0, gint y0, gint width, gint height, - gint c[], gint dc[], guint b0, guint b1, guint mask); -static const guchar *sp_color_slider_render_map (gint x0, gint y0, gint width, gint height, - guchar *map, gint start, gint step, guint b0, guint b1, guint mask); - -static guint slider_signals[LAST_SIGNAL] = {0}; - -G_DEFINE_TYPE(SPColorSlider, sp_color_slider, GTK_TYPE_WIDGET); - -static void sp_color_slider_class_init(SPColorSliderClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS(klass); - GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass); - - slider_signals[GRABBED] = g_signal_new ("grabbed", - G_TYPE_FROM_CLASS(object_class), - (GSignalFlags)(G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE), - G_STRUCT_OFFSET (SPColorSliderClass, grabbed), - NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); - slider_signals[DRAGGED] = g_signal_new ("dragged", - G_TYPE_FROM_CLASS(object_class), - (GSignalFlags)(G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE), - G_STRUCT_OFFSET (SPColorSliderClass, dragged), - NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); - slider_signals[RELEASED] = g_signal_new ("released", - G_TYPE_FROM_CLASS(object_class), - (GSignalFlags)(G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE), - G_STRUCT_OFFSET (SPColorSliderClass, released), - NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); - slider_signals[CHANGED] = g_signal_new ("changed", - G_TYPE_FROM_CLASS(object_class), - (GSignalFlags)(G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE), - G_STRUCT_OFFSET (SPColorSliderClass, changed), - NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); - - object_class->dispose = sp_color_slider_dispose; - - widget_class->realize = sp_color_slider_realize; -#if GTK_CHECK_VERSION(3,0,0) - widget_class->get_preferred_width = sp_color_slider_get_preferred_width; - widget_class->get_preferred_height = sp_color_slider_get_preferred_height; - widget_class->draw = sp_color_slider_draw; -#else - widget_class->size_request = sp_color_slider_size_request; - widget_class->expose_event = sp_color_slider_expose; -#endif - widget_class->size_allocate = sp_color_slider_size_allocate; -/* widget_class->draw_focus = sp_color_slider_draw_focus; */ -/* widget_class->draw_default = sp_color_slider_draw_default; */ - - widget_class->button_press_event = sp_color_slider_button_press; - widget_class->button_release_event = sp_color_slider_button_release; - widget_class->motion_notify_event = sp_color_slider_motion_notify; -} - -static void -sp_color_slider_init (SPColorSlider *slider) -{ - /* We are widget with window */ - gtk_widget_set_has_window (GTK_WIDGET(slider), TRUE); - - slider->dragging = FALSE; - - slider->adjustment = NULL; - slider->value = 0.0; - - slider->c0[0] = 0x00; - slider->c0[1] = 0x00; - slider->c0[2] = 0x00; - slider->c0[3] = 0xff; - - slider->cm[0] = 0xff; - slider->cm[1] = 0x00; - slider->cm[2] = 0x00; - slider->cm[3] = 0xff; - - slider->c1[0] = 0xff; - slider->c1[1] = 0xff; - slider->c1[2] = 0xff; - slider->c1[3] = 0xff; - - slider->b0 = 0x5f; - slider->b1 = 0xa0; - slider->bmask = 0x08; - - slider->map = NULL; -} - -static void sp_color_slider_dispose(GObject *object) -{ - SPColorSlider *slider = SP_COLOR_SLIDER (object); - - if (slider->adjustment) { - g_signal_handlers_disconnect_matched (G_OBJECT (slider->adjustment), G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, slider); - g_object_unref (slider->adjustment); - slider->adjustment = NULL; - } - - if (G_OBJECT_CLASS(sp_color_slider_parent_class)->dispose) - G_OBJECT_CLASS(sp_color_slider_parent_class)->dispose (object); -} - -static void -sp_color_slider_realize (GtkWidget *widget) -{ - GdkWindowAttr attributes; - gint attributes_mask; - GtkAllocation allocation; - - gtk_widget_get_allocation(widget, &allocation); - gtk_widget_set_realized (widget, TRUE); - - attributes.window_type = GDK_WINDOW_CHILD; - attributes.x = allocation.x; - attributes.y = allocation.y; - attributes.width = allocation.width; - attributes.height = allocation.height; - attributes.wclass = GDK_INPUT_OUTPUT; - attributes.visual = gdk_screen_get_system_visual(gdk_screen_get_default()); - -#if !GTK_CHECK_VERSION(3,0,0) - attributes.colormap = gdk_screen_get_system_colormap(gdk_screen_get_default()); -#endif - - attributes.event_mask = gtk_widget_get_events (widget); - attributes.event_mask |= (GDK_EXPOSURE_MASK | - GDK_BUTTON_PRESS_MASK | - GDK_BUTTON_RELEASE_MASK | - GDK_POINTER_MOTION_MASK | - GDK_ENTER_NOTIFY_MASK | - GDK_LEAVE_NOTIFY_MASK); -#if GTK_CHECK_VERSION(3,0,0) - attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL; -#else - attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP; -#endif - - gtk_widget_set_window(widget, - gdk_window_new(gtk_widget_get_parent_window(widget), - &attributes, attributes_mask)); - - gdk_window_set_user_data(gtk_widget_get_window(widget), widget); - -#if !GTK_CHECK_VERSION(3,0,0) - // This doesn't do anything in GTK+ 3 - gtk_widget_set_style(widget, - gtk_style_attach(gtk_widget_get_style(widget), - gtk_widget_get_window(widget))); -#endif -} - -static void -sp_color_slider_size_request (GtkWidget *widget, GtkRequisition *requisition) -{ - GtkStyle *style = gtk_widget_get_style(widget); - requisition->width = SLIDER_WIDTH + style->xthickness * 2; - requisition->height = SLIDER_HEIGHT + style->ythickness * 2; -} - -#if GTK_CHECK_VERSION(3,0,0) -static void sp_color_slider_get_preferred_width(GtkWidget *widget, gint *minimal_width, gint *natural_width) -{ - GtkRequisition requisition; - sp_color_slider_size_request(widget, &requisition); - *minimal_width = *natural_width = requisition.width; -} - -static void sp_color_slider_get_preferred_height(GtkWidget *widget, gint *minimal_height, gint *natural_height) -{ - GtkRequisition requisition; - sp_color_slider_size_request(widget, &requisition); - *minimal_height = *natural_height = requisition.height; -} -#endif - -static void -sp_color_slider_size_allocate (GtkWidget *widget, GtkAllocation *allocation) -{ - gtk_widget_set_allocation(widget, allocation); - - if (gtk_widget_get_realized (widget)) { - /* Resize GdkWindow */ - gdk_window_move_resize(gtk_widget_get_window(widget), - allocation->x, allocation->y, - allocation->width, allocation->height); - } -} - -#if !GTK_CHECK_VERSION(3,0,0) -static gboolean sp_color_slider_expose(GtkWidget *widget, GdkEventExpose * /*event*/) -{ - gboolean result = FALSE; - - if (gtk_widget_is_drawable(widget)) { - GdkWindow *window = gtk_widget_get_window(widget); - cairo_t *cr = gdk_cairo_create(window); - result = sp_color_slider_draw(widget, cr); - cairo_destroy(cr); - } - - return result; -} -#endif - -static gint -sp_color_slider_button_press (GtkWidget *widget, GdkEventButton *event) -{ - SPColorSlider *slider; - - slider = SP_COLOR_SLIDER (widget); - - if (event->button == 1) { - GtkAllocation allocation; - gtk_widget_get_allocation(widget, &allocation); - gint cx, cw; - cx = gtk_widget_get_style(widget)->xthickness; - cw = allocation.width - 2 * cx; - g_signal_emit (G_OBJECT (slider), slider_signals[GRABBED], 0); - slider->dragging = TRUE; - slider->oldvalue = slider->value; - ColorScales::setScaled( slider->adjustment, CLAMP ((gfloat) (event->x - cx) / cw, 0.0, 1.0) ); - g_signal_emit (G_OBJECT (slider), slider_signals[DRAGGED], 0); - -#if GTK_CHECK_VERSION(3,0,0) - gdk_device_grab(gdk_event_get_device(reinterpret_cast<GdkEvent *>(event)), - gtk_widget_get_window(widget), - GDK_OWNERSHIP_NONE, - FALSE, - static_cast<GdkEventMask>(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK), - NULL, - event->time); -#else - gdk_pointer_grab(gtk_widget_get_window(widget), FALSE, - static_cast<GdkEventMask>(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK), - NULL, NULL, event->time); -#endif - } - - return FALSE; -} - -static gint -sp_color_slider_button_release (GtkWidget *widget, GdkEventButton *event) -{ - SPColorSlider *slider; - - slider = SP_COLOR_SLIDER (widget); - - if (event->button == 1) { - -#if GTK_CHECK_VERSION(3,0,0) - gdk_device_ungrab(gdk_event_get_device(reinterpret_cast<GdkEvent *>(event)), - gdk_event_get_time(reinterpret_cast<GdkEvent *>(event))); -#else - gdk_pointer_ungrab (event->time); -#endif - - slider->dragging = FALSE; - g_signal_emit (G_OBJECT (slider), slider_signals[RELEASED], 0); - if (slider->value != slider->oldvalue) g_signal_emit (G_OBJECT (slider), slider_signals[CHANGED], 0); - } - - return FALSE; -} - -static gint -sp_color_slider_motion_notify (GtkWidget *widget, GdkEventMotion *event) -{ - SPColorSlider *slider; - - slider = SP_COLOR_SLIDER (widget); - - if (slider->dragging) { - gint cx, cw; - GtkAllocation allocation; - gtk_widget_get_allocation(widget, &allocation); - cx = gtk_widget_get_style(widget)->xthickness; - cw = allocation.width - 2 * cx; - ColorScales::setScaled( slider->adjustment, CLAMP ((gfloat) (event->x - cx) / cw, 0.0, 1.0) ); - g_signal_emit (G_OBJECT (slider), slider_signals[DRAGGED], 0); - } - - return FALSE; -} - -GtkWidget *sp_color_slider_new(GtkAdjustment *adjustment) -{ - SPColorSlider *slider = SP_COLOR_SLIDER(g_object_new(SP_TYPE_COLOR_SLIDER, NULL)); - - sp_color_slider_set_adjustment (slider, adjustment); - - return GTK_WIDGET (slider); -} - -void sp_color_slider_set_adjustment(SPColorSlider *slider, GtkAdjustment *adjustment) -{ - g_return_if_fail (slider != NULL); - g_return_if_fail (SP_IS_COLOR_SLIDER (slider)); - - if (!adjustment) { - adjustment = GTK_ADJUSTMENT(gtk_adjustment_new(0.0, 0.0, 1.0, 0.01, 0.0, 0.0)); - } else { - gtk_adjustment_set_page_increment(adjustment, 0.0); - gtk_adjustment_set_page_size(adjustment, 0.0); - } - - if (slider->adjustment != adjustment) { - if (slider->adjustment) { - g_signal_handlers_disconnect_matched (G_OBJECT (slider->adjustment), G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, slider); - g_object_unref (slider->adjustment); - } - - slider->adjustment = adjustment; - g_object_ref (adjustment); - g_object_ref_sink (adjustment); - - g_signal_connect (G_OBJECT (adjustment), "changed", - G_CALLBACK (sp_color_slider_adjustment_changed), slider); - g_signal_connect (G_OBJECT (adjustment), "value_changed", - G_CALLBACK (sp_color_slider_adjustment_value_changed), slider); - - slider->value = ColorScales::getScaled( adjustment ); - - sp_color_slider_adjustment_changed (adjustment, slider); - } -} - -void -sp_color_slider_set_colors (SPColorSlider *slider, guint32 start, guint32 mid, guint32 end) -{ - g_return_if_fail (slider != NULL); - g_return_if_fail (SP_IS_COLOR_SLIDER (slider)); - - // Remove any map, if set - slider->map = 0; - - slider->c0[0] = start >> 24; - slider->c0[1] = (start >> 16) & 0xff; - slider->c0[2] = (start >> 8) & 0xff; - slider->c0[3] = start & 0xff; - - slider->cm[0] = mid >> 24; - slider->cm[1] = (mid >> 16) & 0xff; - slider->cm[2] = (mid >> 8) & 0xff; - slider->cm[3] = mid & 0xff; - - slider->c1[0] = end >> 24; - slider->c1[1] = (end >> 16) & 0xff; - slider->c1[2] = (end >> 8) & 0xff; - slider->c1[3] = end & 0xff; - - gtk_widget_queue_draw (GTK_WIDGET (slider)); -} - -void -sp_color_slider_set_map (SPColorSlider *slider, const guchar *map) -{ - g_return_if_fail (slider != NULL); - g_return_if_fail (SP_IS_COLOR_SLIDER (slider)); - - slider->map = const_cast<guchar *>(map); - - gtk_widget_queue_draw (GTK_WIDGET (slider)); -} - -void -sp_color_slider_set_background (SPColorSlider *slider, guint dark, guint light, guint size) -{ - g_return_if_fail (slider != NULL); - g_return_if_fail (SP_IS_COLOR_SLIDER (slider)); - - slider->b0 = dark; - slider->b1 = light; - slider->bmask = size; - - gtk_widget_queue_draw (GTK_WIDGET (slider)); -} - -static void -sp_color_slider_adjustment_changed (GtkAdjustment */*adjustment*/, SPColorSlider *slider) -{ - gtk_widget_queue_draw (GTK_WIDGET (slider)); -} - -static void -sp_color_slider_adjustment_value_changed (GtkAdjustment *adjustment, SPColorSlider *slider) -{ - GtkWidget *widget; - - widget = GTK_WIDGET (slider); - - if (slider->value != ColorScales::getScaled( adjustment )) { - gint cx, cy, cw, ch; - GtkStyle *style = gtk_widget_get_style(widget); - GtkAllocation allocation; - gtk_widget_get_allocation(widget, &allocation); - cx = style->xthickness; - cy = style->ythickness; - cw = allocation.width - 2 * cx; - ch = allocation.height - 2 * cy; - if ((gint) (ColorScales::getScaled( adjustment ) * cw) != (gint) (slider->value * cw)) { - gint ax, ay; - gfloat value; - value = slider->value; - slider->value = ColorScales::getScaled( adjustment ); - ax = (int)(cx + value * cw - ARROW_SIZE / 2 - 2); - ay = cy; - gtk_widget_queue_draw_area (widget, ax, ay, ARROW_SIZE + 4, ch); - ax = (int)(cx + slider->value * cw - ARROW_SIZE / 2 - 2); - ay = cy; - gtk_widget_queue_draw_area (widget, ax, ay, ARROW_SIZE + 4, ch); - } else { - slider->value = ColorScales::getScaled( adjustment ); - } - } -} - -static gboolean sp_color_slider_draw(GtkWidget *widget, cairo_t *cr) -{ - SPColorSlider *slider = SP_COLOR_SLIDER(widget); - - gboolean colorsOnTop = Inkscape::Preferences::get()->getBool("/options/workarounds/colorsontop", false); - - GtkAllocation allocation; - gtk_widget_get_allocation(widget, &allocation); - -#if GTK_CHECK_VERSION(3,0,0) - GtkStyleContext *context = gtk_widget_get_style_context(widget); -#else - GdkWindow *window = gtk_widget_get_window(widget); - GtkStyle *style = gtk_widget_get_style(widget); -#endif - - // Draw shadow - if (colorsOnTop) { -#if GTK_CHECK_VERSION(3,0,0) - gtk_render_frame(context, - cr, - 0, 0, - allocation.width, allocation.height); -#else - gtk_paint_shadow( style, window, - gtk_widget_get_state(widget), GTK_SHADOW_IN, - NULL, widget, "colorslider", - 0, 0, - allocation.width, allocation.height); -#endif - } - - /* Paintable part of color gradient area */ - GdkRectangle carea; - -#if GTK_CHECK_VERSION(3,0,0) - GtkBorder padding; - - gtk_style_context_get_padding(context, - gtk_widget_get_state_flags(widget), - &padding); - - carea.x = padding.left; - carea.y = padding.top; -#else - carea.x = style->xthickness; - carea.y = style->ythickness; -#endif - - carea.width = allocation.width - 2 * carea.x; - carea.height = allocation.height - 2 * carea.y; - - if (slider->map) { - /* Render map pixelstore */ - gint d = (1024 << 16) / carea.width; - gint s = 0; - - const guchar *b = sp_color_slider_render_map(0, 0, carea.width, carea.height, - slider->map, s, d, - slider->b0, slider->b1, slider->bmask); - - if (b != NULL && carea.width > 0) { - GdkPixbuf *pb = gdk_pixbuf_new_from_data (b, GDK_COLORSPACE_RGB, - 0, 8, carea.width, carea.height, carea.width * 3, NULL, NULL); - - gdk_cairo_set_source_pixbuf(cr, pb, carea.x, carea.y); - cairo_paint(cr); - g_object_unref(pb); - } - - } else { - gint c[4], dc[4]; - - /* Render gradient */ - - // part 1: from c0 to cm - if (carea.width > 0) { - for (gint i = 0; i < 4; i++) { - c[i] = slider->c0[i] << 16; - dc[i] = ((slider->cm[i] << 16) - c[i]) / (carea.width/2); - } - guint wi = carea.width/2; - const guchar *b = sp_color_slider_render_gradient(0, 0, wi, carea.height, - c, dc, slider->b0, slider->b1, slider->bmask); - - /* Draw pixelstore 1 */ - if (b != NULL && wi > 0) { - GdkPixbuf *pb = gdk_pixbuf_new_from_data (b, GDK_COLORSPACE_RGB, - 0, 8, wi, carea.height, wi * 3, NULL, NULL); - - gdk_cairo_set_source_pixbuf(cr, pb, carea.x, carea.y); - cairo_paint(cr); - g_object_unref(pb); - } - } - - // part 2: from cm to c1 - if (carea.width > 0) { - for (gint i = 0; i < 4; i++) { - c[i] = slider->cm[i] << 16; - dc[i] = ((slider->c1[i] << 16) - c[i]) / (carea.width/2); - } - guint wi = carea.width/2; - const guchar *b = sp_color_slider_render_gradient(carea.width/2, 0, wi, carea.height, - c, dc, - slider->b0, slider->b1, slider->bmask); - - /* Draw pixelstore 2 */ - if (b != NULL && wi > 0) { - GdkPixbuf *pb = gdk_pixbuf_new_from_data (b, GDK_COLORSPACE_RGB, - 0, 8, wi, carea.height, wi * 3, NULL, NULL); - - gdk_cairo_set_source_pixbuf(cr, pb, carea.width/2 + carea.x, carea.y); - cairo_paint(cr); - - g_object_unref(pb); - } - } - } - - /* Draw shadow */ - if (!colorsOnTop) { -#if GTK_CHECK_VERSION(3,0,0) - gtk_render_frame(context, - cr, - 0, 0, - allocation.width, allocation.height); -#else - gtk_paint_shadow( style, window, - gtk_widget_get_state(widget), GTK_SHADOW_IN, - NULL, widget, "colorslider", - 0, 0, - allocation.width, allocation.height); -#endif - } - - /* Draw arrow */ - gint x = (int)(slider->value * (carea.width - 1) - ARROW_SIZE / 2 + carea.x); - gint y1 = carea.y; - gint y2 = carea.y + carea.height - 1; - cairo_set_line_width(cr, 1.0); - - // Define top arrow - cairo_move_to(cr, x - 0.5, y1 + 0.5); - cairo_line_to(cr, x + ARROW_SIZE - 0.5, y1 + 0.5); - cairo_line_to(cr, x + (ARROW_SIZE-1)/2.0, y1 + ARROW_SIZE/2.0 + 0.5); - cairo_line_to(cr, x - 0.5, y1 + 0.5); - - // Define bottom arrow - cairo_move_to(cr, x - 0.5, y2 + 0.5); - cairo_line_to(cr, x + ARROW_SIZE - 0.5, y2 + 0.5); - cairo_line_to(cr, x + (ARROW_SIZE-1)/2.0, y2 - ARROW_SIZE/2.0 + 0.5); - cairo_line_to(cr, x - 0.5, y2 + 0.5); - - // Render both arrows - cairo_set_source_rgb(cr, 1.0, 1.0, 1.0); - cairo_stroke_preserve(cr); - cairo_set_source_rgb(cr, 0.0, 0.0, 0.0); - cairo_fill(cr); - - return FALSE; -} - -/* Colors are << 16 */ - -static const guchar * -sp_color_slider_render_gradient (gint x0, gint y0, gint width, gint height, - gint c[], gint dc[], guint b0, guint b1, guint mask) -{ - static guchar *buf = NULL; - static gint bs = 0; - guchar *dp; - gint x, y; - guint r, g, b, a; - - if (buf && (bs < width * height)) { - g_free (buf); - buf = NULL; - } - if (!buf) { - buf = g_new (guchar, width * height * 3); - bs = width * height; - } - - dp = buf; - r = c[0]; - g = c[1]; - b = c[2]; - a = c[3]; - for (x = x0; x < x0 + width; x++) { - gint cr, cg, cb, ca; - guchar *d; - cr = r >> 16; - cg = g >> 16; - cb = b >> 16; - ca = a >> 16; - d = dp; - for (y = y0; y < y0 + height; y++) { - guint bg, fc; - /* Background value */ - bg = ((x & mask) ^ (y & mask)) ? b0 : b1; - fc = (cr - bg) * ca; - d[0] = bg + ((fc + (fc >> 8) + 0x80) >> 8); - fc = (cg - bg) * ca; - d[1] = bg + ((fc + (fc >> 8) + 0x80) >> 8); - fc = (cb - bg) * ca; - d[2] = bg + ((fc + (fc >> 8) + 0x80) >> 8); - d += 3 * width; - } - r += dc[0]; - g += dc[1]; - b += dc[2]; - a += dc[3]; - dp += 3; - } - - return buf; -} - -/* Positions are << 16 */ - -static const guchar * -sp_color_slider_render_map (gint x0, gint y0, gint width, gint height, - guchar *map, gint start, gint step, guint b0, guint b1, guint mask) -{ - static guchar *buf = NULL; - static gint bs = 0; - guchar *dp; - gint x, y; - - if (buf && (bs < width * height)) { - g_free (buf); - buf = NULL; - } - if (!buf) { - buf = g_new (guchar, width * height * 3); - bs = width * height; - } - - dp = buf; - for (x = x0; x < x0 + width; x++) { - gint cr, cg, cb, ca; - guchar *d = dp; - guchar *sp = map + 4 * (start >> 16); - cr = *sp++; - cg = *sp++; - cb = *sp++; - ca = *sp++; - for (y = y0; y < y0 + height; y++) { - guint bg, fc; - /* Background value */ - bg = ((x & mask) ^ (y & mask)) ? b0 : b1; - fc = (cr - bg) * ca; - d[0] = bg + ((fc + (fc >> 8) + 0x80) >> 8); - fc = (cg - bg) * ca; - d[1] = bg + ((fc + (fc >> 8) + 0x80) >> 8); - fc = (cb - bg) * ca; - d[2] = bg + ((fc + (fc >> 8) + 0x80) >> 8); - d += 3 * width; - } - dp += 3; - start += step; - } - - return buf; -} - diff --git a/src/widgets/sp-color-slider.h b/src/widgets/sp-color-slider.h deleted file mode 100644 index b81d62e41..000000000 --- a/src/widgets/sp-color-slider.h +++ /dev/null @@ -1,60 +0,0 @@ -#ifndef __SP_COLOR_SLIDER_H__ -#define __SP_COLOR_SLIDER_H__ - -/* - * A slider with colored background - * - * Author: - * Lauris Kaplinski <lauris@kaplinski.com> - * - * Copyright (C) 2001-2002 Lauris Kaplinski - * - * This code is in public domain - */ - -#include <gtk/gtk.h> - -#define SP_TYPE_COLOR_SLIDER (sp_color_slider_get_type ()) -#define SP_COLOR_SLIDER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), SP_TYPE_COLOR_SLIDER, SPColorSlider)) -#define SP_COLOR_SLIDER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), SP_TYPE_COLOR_SLIDER, SPColorSliderClass)) -#define SP_IS_COLOR_SLIDER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), SP_TYPE_COLOR_SLIDER)) -#define SP_IS_COLOR_SLIDER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), SP_TYPE_COLOR_SLIDER)) - -struct SPColorSlider { - GtkWidget widget; - - guint dragging : 1; - - GtkAdjustment *adjustment; - - gfloat value; - gfloat oldvalue; - guchar c0[4], cm[4], c1[4]; - guchar b0, b1; - guchar bmask; - - gint mapsize; - guchar *map; -}; - -struct SPColorSliderClass { - GtkWidgetClass parent_class; - - void (* grabbed) (SPColorSlider *slider); - void (* dragged) (SPColorSlider *slider); - void (* released) (SPColorSlider *slider); - void (* changed) (SPColorSlider *slider); -}; - -GType sp_color_slider_get_type (void); - -GtkWidget *sp_color_slider_new (GtkAdjustment *adjustment); - -void sp_color_slider_set_adjustment (SPColorSlider *slider, GtkAdjustment *adjustment); -void sp_color_slider_set_colors (SPColorSlider *slider, guint32 start, guint32 mid, guint32 end); -void sp_color_slider_set_map (SPColorSlider *slider, const guchar *map); -void sp_color_slider_set_background (SPColorSlider *slider, guint dark, guint light, guint size); - - - -#endif diff --git a/src/widgets/sp-color-wheel-selector.cpp b/src/widgets/sp-color-wheel-selector.cpp deleted file mode 100644 index 6cfa7c14d..000000000 --- a/src/widgets/sp-color-wheel-selector.cpp +++ /dev/null @@ -1,348 +0,0 @@ -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif -#include <math.h> -#include <gtk/gtk.h> -#include <glibmm/i18n.h> -#include "ui/dialog-events.h" -#include "sp-color-wheel-selector.h" -#include "sp-color-scales.h" -#include "sp-color-slider.h" -#include "sp-color-icc-selector.h" -#include "../svg/svg-icc-color.h" -#include "ui/widget/gimpcolorwheel.h" - -G_BEGIN_DECLS - -static void sp_color_wheel_selector_dispose(GObject *object); - -static void sp_color_wheel_selector_show_all (GtkWidget *widget); -static void sp_color_wheel_selector_hide(GtkWidget *widget); - - -G_END_DECLS - -#define XPAD 4 -#define YPAD 1 - -G_DEFINE_TYPE(SPColorWheelSelector, sp_color_wheel_selector, SP_TYPE_COLOR_SELECTOR); - -static void sp_color_wheel_selector_class_init(SPColorWheelSelectorClass *klass) -{ - static const gchar* nameset[] = {N_("Wheel"), 0}; - GObjectClass *object_class = G_OBJECT_CLASS(klass); - GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass); - SPColorSelectorClass *selector_class = SP_COLOR_SELECTOR_CLASS (klass); - - selector_class->name = nameset; - selector_class->submode_count = 1; - - object_class->dispose = sp_color_wheel_selector_dispose; - - widget_class->show_all = sp_color_wheel_selector_show_all; - widget_class->hide = sp_color_wheel_selector_hide; -} - -ColorWheelSelector::ColorWheelSelector( SPColorSelector* csel ) - : ColorSelector( csel ), - _updating( FALSE ), - _dragging( FALSE ), - _adj(0), - _wheel(0), - _slider(0), - _sbtn(0), - _label(0) -{ -} - -ColorWheelSelector::~ColorWheelSelector() -{ - _adj = 0; - _wheel = 0; - _sbtn = 0; - _label = 0; -} - -void sp_color_wheel_selector_init (SPColorWheelSelector *cs) -{ - SP_COLOR_SELECTOR(cs)->base = new ColorWheelSelector( SP_COLOR_SELECTOR(cs) ); - - if ( SP_COLOR_SELECTOR(cs)->base ) - { - SP_COLOR_SELECTOR(cs)->base->init(); - } -} - -void ColorWheelSelector::init() -{ - gint row = 0; - - _updating = FALSE; - _dragging = FALSE; - -#if GTK_CHECK_VERSION(3,0,0) - GtkWidget *t = gtk_grid_new(); -#else - GtkWidget *t = gtk_table_new (5, 3, FALSE); -#endif - - gtk_widget_show (t); - gtk_box_pack_start (GTK_BOX (_csel), t, TRUE, TRUE, 0); - - /* Create components */ - row = 0; - - _wheel = gimp_color_wheel_new(); - gtk_widget_show( _wheel ); - -#if GTK_CHECK_VERSION(3,0,0) - gtk_widget_set_halign(_wheel, GTK_ALIGN_FILL); - gtk_widget_set_valign(_wheel, GTK_ALIGN_FILL); - gtk_widget_set_hexpand(_wheel, TRUE); - gtk_widget_set_vexpand(_wheel, TRUE); - gtk_grid_attach(GTK_GRID(t), _wheel, 0, row, 3, 1); -#else - gtk_table_attach(GTK_TABLE(t), _wheel, 0, 3, row, row + 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0); -#endif - - row++; - - /* Label */ - _label = gtk_label_new_with_mnemonic (_("_A:")); - gtk_misc_set_alignment (GTK_MISC (_label), 1.0, 0.5); - gtk_widget_show (_label); - -#if GTK_CHECK_VERSION(3,0,0) - #if GTK_CHECK_VERSION(3,12,0) - gtk_widget_set_margin_start(_label, XPAD); - gtk_widget_set_margin_end(_label, XPAD); - #else - gtk_widget_set_margin_left(_label, XPAD); - gtk_widget_set_margin_right(_label, XPAD); - #endif - gtk_widget_set_margin_top(_label, YPAD); - gtk_widget_set_margin_bottom(_label, YPAD); - gtk_widget_set_halign(_label, GTK_ALIGN_FILL); - gtk_widget_set_valign(_label, GTK_ALIGN_FILL); - gtk_grid_attach(GTK_GRID(t), _label, 0, row, 1, 1); -#else - gtk_table_attach (GTK_TABLE (t), _label, 0, 1, row, row + 1, GTK_FILL, GTK_FILL, XPAD, YPAD); -#endif - - /* Adjustment */ - _adj = GTK_ADJUSTMENT(gtk_adjustment_new(0.0, 0.0, 255.0, 1.0, 10.0, 10.0)); - - /* Slider */ - _slider = sp_color_slider_new (_adj); - gtk_widget_set_tooltip_text (_slider, _("Alpha (opacity)")); - gtk_widget_show (_slider); - -#if GTK_CHECK_VERSION(3,0,0) - #if GTK_CHECK_VERSION(3,12,0) - gtk_widget_set_margin_start(_slider, XPAD); - gtk_widget_set_margin_end(_slider, XPAD); - #else - gtk_widget_set_margin_left(_slider, XPAD); - gtk_widget_set_margin_right(_slider, XPAD); - #endif - gtk_widget_set_margin_top(_slider, YPAD); - gtk_widget_set_margin_bottom(_slider, YPAD); - gtk_widget_set_hexpand(_slider, TRUE); - gtk_widget_set_halign(_slider, GTK_ALIGN_FILL); - gtk_widget_set_valign(_slider, GTK_ALIGN_FILL); - gtk_grid_attach(GTK_GRID(t), _slider, 1, row, 1, 1); -#else - gtk_table_attach(GTK_TABLE (t), _slider, 1, 2, row, row + 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), GTK_FILL, XPAD, YPAD); -#endif - - sp_color_slider_set_colors (SP_COLOR_SLIDER (_slider), - SP_RGBA32_F_COMPOSE (1.0, 1.0, 1.0, 0.0), - SP_RGBA32_F_COMPOSE (1.0, 1.0, 1.0, 0.5), - SP_RGBA32_F_COMPOSE (1.0, 1.0, 1.0, 1.0)); - - - /* Spinbutton */ - _sbtn = gtk_spin_button_new (GTK_ADJUSTMENT (_adj), 1.0, 0); - gtk_widget_set_tooltip_text (_sbtn, _("Alpha (opacity)")); - sp_dialog_defocus_on_enter (_sbtn); - gtk_label_set_mnemonic_widget (GTK_LABEL(_label), _sbtn); - gtk_widget_show (_sbtn); - -#if GTK_CHECK_VERSION(3,0,0) - #if GTK_CHECK_VERSION(3,12,0) - gtk_widget_set_margin_start(_sbtn, XPAD); - gtk_widget_set_margin_end(_sbtn, XPAD); - #else - gtk_widget_set_margin_left(_sbtn, XPAD); - gtk_widget_set_margin_right(_sbtn, XPAD); - #endif - gtk_widget_set_margin_top(_sbtn, YPAD); - gtk_widget_set_margin_bottom(_sbtn, YPAD); - gtk_widget_set_halign(_sbtn, GTK_ALIGN_CENTER); - gtk_widget_set_valign(_sbtn, GTK_ALIGN_CENTER); - gtk_grid_attach(GTK_GRID(t), _sbtn, 2, row, 1, 1); -#else - gtk_table_attach (GTK_TABLE (t), _sbtn, 2, 3, row, row + 1, (GtkAttachOptions)0, (GtkAttachOptions)0, XPAD, YPAD); -#endif - - /* Signals */ - g_signal_connect (G_OBJECT (_adj), "value_changed", - G_CALLBACK (_adjustmentChanged), _csel); - - g_signal_connect (G_OBJECT (_slider), "grabbed", - G_CALLBACK (_sliderGrabbed), _csel); - g_signal_connect (G_OBJECT (_slider), "released", - G_CALLBACK (_sliderReleased), _csel); - g_signal_connect (G_OBJECT (_slider), "changed", - G_CALLBACK (_sliderChanged), _csel); - - g_signal_connect( G_OBJECT(_wheel), "changed", - G_CALLBACK (_wheelChanged), _csel ); -} - -static void sp_color_wheel_selector_dispose(GObject *object) -{ - if (G_OBJECT_CLASS(sp_color_wheel_selector_parent_class)->dispose) - G_OBJECT_CLASS(sp_color_wheel_selector_parent_class)->dispose(object); -} - -static void -sp_color_wheel_selector_show_all (GtkWidget *widget) -{ - gtk_widget_show (widget); -} - -static void sp_color_wheel_selector_hide(GtkWidget *widget) -{ - gtk_widget_hide(widget); -} - -GtkWidget *sp_color_wheel_selector_new() -{ - SPColorWheelSelector *csel = SP_COLOR_WHEEL_SELECTOR(g_object_new (SP_TYPE_COLOR_WHEEL_SELECTOR, NULL)); - - return GTK_WIDGET (csel); -} - -/* Helpers for setting color value */ - -static void preserve_icc(SPColor *color, SPColorWheelSelector *cs){ - ColorSelector* selector = static_cast<ColorSelector*>(SP_COLOR_SELECTOR(cs)->base); - color->icc = selector->getColor().icc ? new SVGICCColor(*selector->getColor().icc) : 0; -} - -void ColorWheelSelector::_colorChanged() -{ -#ifdef DUMP_CHANGE_INFO - g_message("ColorWheelSelector::_colorChanged( this=%p, %f, %f, %f, %f)", this, color.v.c[0], color.v.c[1], color.v.c[2], alpha ); -#endif - _updating = TRUE; - { - float hsv[3] = {0,0,0}; - sp_color_rgb_to_hsv_floatv(hsv, _color.v.c[0], _color.v.c[1], _color.v.c[2]); - gimp_color_wheel_set_color( GIMP_COLOR_WHEEL(_wheel), hsv[0], hsv[1], hsv[2] ); - } - - guint32 start = _color.toRGBA32( 0x00 ); - guint32 mid = _color.toRGBA32( 0x7f ); - guint32 end = _color.toRGBA32( 0xff ); - - sp_color_slider_set_colors(SP_COLOR_SLIDER(_slider), start, mid, end); - - ColorScales::setScaled(_adj, _alpha); - - _updating = FALSE; -} - -void ColorWheelSelector::_adjustmentChanged( GtkAdjustment *adjustment, SPColorWheelSelector *cs ) -{ -// TODO check this. It looks questionable: - // if a value is entered between 0 and 1 exclusive, normalize it to (int) 0..255 or 0..100 - gdouble value = gtk_adjustment_get_value (adjustment); - gdouble upper = gtk_adjustment_get_upper (adjustment); - - if (value > 0.0 && value < 1.0) { - gtk_adjustment_set_value( adjustment, floor (value * upper + 0.5) ); - } - - ColorWheelSelector* wheelSelector = static_cast<ColorWheelSelector*>(SP_COLOR_SELECTOR(cs)->base); - if (wheelSelector->_updating) return; - - wheelSelector->_updating = TRUE; - - preserve_icc(&wheelSelector->_color, cs); - wheelSelector->_updateInternals( wheelSelector->_color, ColorScales::getScaled( wheelSelector->_adj ), wheelSelector->_dragging ); - - wheelSelector->_updating = FALSE; -} - -void ColorWheelSelector::_sliderGrabbed( SPColorSlider *slider, SPColorWheelSelector *cs ) -{ - (void)slider; - ColorWheelSelector* wheelSelector = static_cast<ColorWheelSelector*>(SP_COLOR_SELECTOR(cs)->base); - if (!wheelSelector->_dragging) { - wheelSelector->_dragging = TRUE; - wheelSelector->_grabbed(); - - preserve_icc(&wheelSelector->_color, cs); - wheelSelector->_updateInternals( wheelSelector->_color, ColorScales::getScaled( wheelSelector->_adj ), wheelSelector->_dragging ); - } -} - -void ColorWheelSelector::_sliderReleased( SPColorSlider *slider, SPColorWheelSelector *cs ) -{ - (void)slider; - ColorWheelSelector* wheelSelector = static_cast<ColorWheelSelector*>(SP_COLOR_SELECTOR(cs)->base); - if (wheelSelector->_dragging) { - wheelSelector->_dragging = FALSE; - wheelSelector->_released(); - - preserve_icc(&wheelSelector->_color, cs); - wheelSelector->_updateInternals( wheelSelector->_color, ColorScales::getScaled( wheelSelector->_adj ), wheelSelector->_dragging ); - } -} - -void ColorWheelSelector::_sliderChanged( SPColorSlider *slider, SPColorWheelSelector *cs ) -{ - (void)slider; - ColorWheelSelector* wheelSelector = static_cast<ColorWheelSelector*>(SP_COLOR_SELECTOR(cs)->base); - - preserve_icc(&wheelSelector->_color, cs); - wheelSelector->_updateInternals( wheelSelector->_color, ColorScales::getScaled( wheelSelector->_adj ), wheelSelector->_dragging ); -} - -void ColorWheelSelector::_wheelChanged( GimpColorWheel *wheel, SPColorWheelSelector *cs ) -{ - ColorWheelSelector* wheelSelector = static_cast<ColorWheelSelector*>(SP_COLOR_SELECTOR(cs)->base); - - gdouble h = 0; - gdouble s = 0; - gdouble v = 0; - gimp_color_wheel_get_color( wheel, &h, &s, &v ); - - float rgb[3] = {0,0,0}; - sp_color_hsv_to_rgb_floatv (rgb, h, s, v); - - SPColor color(rgb[0], rgb[1], rgb[2]); - - guint32 start = color.toRGBA32( 0x00 ); - guint32 mid = color.toRGBA32( 0x7f ); - guint32 end = color.toRGBA32( 0xff ); - - sp_color_slider_set_colors (SP_COLOR_SLIDER(wheelSelector->_slider), start, mid, end); - - preserve_icc(&color, cs); - wheelSelector->_updateInternals( color, wheelSelector->_alpha, gimp_color_wheel_is_adjusting(wheel) ); -} - - -/* - 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/widgets/sp-color-wheel-selector.h b/src/widgets/sp-color-wheel-selector.h deleted file mode 100644 index 12b060dbe..000000000 --- a/src/widgets/sp-color-wheel-selector.h +++ /dev/null @@ -1,82 +0,0 @@ -#ifndef SEEN_SP_COLOR_WHEEL_SELECTOR_H -#define SEEN_SP_COLOR_WHEEL_SELECTOR_H - -#include <gtk/gtk.h> - -#include "sp-color-selector.h" - -typedef struct _GimpColorWheel GimpColorWheel; -struct SPColorSlider; -struct SPColorWheelSelector; -struct SPColorWheelSelectorClass; - -class ColorWheelSelector: public ColorSelector -{ -public: - ColorWheelSelector( SPColorSelector* csel ); - virtual ~ColorWheelSelector(); - - virtual void init(); - -protected: - virtual void _colorChanged(); - - static void _adjustmentChanged ( GtkAdjustment *adjustment, SPColorWheelSelector *cs ); - - static void _sliderGrabbed( SPColorSlider *slider, SPColorWheelSelector *cs ); - static void _sliderReleased( SPColorSlider *slider, SPColorWheelSelector *cs ); - static void _sliderChanged( SPColorSlider *slider, SPColorWheelSelector *cs ); - static void _wheelChanged( GimpColorWheel *wheel, SPColorWheelSelector *cs ); - - static void _fooChanged( GtkWidget foo, SPColorWheelSelector *cs ); - - void _recalcColor( gboolean changing ); - - gboolean _updating : 1; - gboolean _dragging : 1; - GtkAdjustment* _adj; // Channel adjustment - GtkWidget* _wheel; - GtkWidget* _slider; - GtkWidget* _sbtn; // Spinbutton - GtkWidget* _label; // Label - -private: - // By default, disallow copy constructor and assignment operator - ColorWheelSelector( const ColorWheelSelector& obj ); - ColorWheelSelector& operator=( const ColorWheelSelector& obj ); -}; - - - -#define SP_TYPE_COLOR_WHEEL_SELECTOR (sp_color_wheel_selector_get_type ()) -#define SP_COLOR_WHEEL_SELECTOR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), SP_TYPE_COLOR_WHEEL_SELECTOR, SPColorWheelSelector)) -#define SP_COLOR_WHEEL_SELECTOR_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), SP_TYPE_COLOR_WHEEL_SELECTOR, SPColorWheelSelectorClass)) -#define SP_IS_COLOR_WHEEL_SELECTOR(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), SP_TYPE_COLOR_WHEEL_SELECTOR)) -#define SP_IS_COLOR_WHEEL_SELECTOR_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), SP_TYPE_COLOR_WHEEL_SELECTOR)) - -struct SPColorWheelSelector { - SPColorSelector parent; -}; - -struct SPColorWheelSelectorClass { - SPColorSelectorClass parent_class; -}; - -GType sp_color_wheel_selector_get_type (void); - -GtkWidget *sp_color_wheel_selector_new (void); - - - -#endif // SEEN_SP_COLOR_WHEEL_SELECTOR_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/widgets/spiral-toolbar.cpp b/src/widgets/spiral-toolbar.cpp index 3fb0015c1..751a60f06 100644 --- a/src/widgets/spiral-toolbar.cpp +++ b/src/widgets/spiral-toolbar.cpp @@ -79,11 +79,9 @@ static void sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, Glib::ustr gchar* namespaced_name = g_strconcat("sodipodi:", value_name.data(), NULL); bool modmade = false; - for (GSList const *items = desktop->getSelection()->itemList(); - items != NULL; - items = items->next) - { - SPItem *item = reinterpret_cast<SPItem*>(items->data); + std::vector<SPItem*> itemlist=desktop->getSelection()->itemList(); + for(std::vector<SPItem*>::const_iterator i=itemlist.begin();i!=itemlist.end();i++){ + SPItem *item = *i; if (SP_IS_SPIRAL(item)) { Inkscape::XML::Node *repr = item->getRepr(); sp_repr_set_svg_double( repr, namespaced_name, @@ -197,11 +195,9 @@ static void sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, purge_repr_listener( tbl, tbl ); - for (GSList const *items = selection->itemList(); - items != NULL; - items = items->next) - { - SPItem *item = reinterpret_cast<SPItem*>(items->data); + std::vector<SPItem*> itemlist=selection->itemList(); + for(std::vector<SPItem*>::const_iterator i=itemlist.begin();i!=itemlist.end();i++){ + SPItem *item = *i; if (SP_IS_SPIRAL(item)) { n_selected++; repr = item->getRepr(); diff --git a/src/widgets/spw-utilities.cpp b/src/widgets/spw-utilities.cpp index f87889bb1..7030753a5 100644 --- a/src/widgets/spw-utilities.cpp +++ b/src/widgets/spw-utilities.cpp @@ -17,10 +17,6 @@ #include <cstring> #include <string> -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include <gtkmm/box.h> #include <gtkmm/label.h> diff --git a/src/widgets/star-toolbar.cpp b/src/widgets/star-toolbar.cpp index cf12391c1..96005d7df 100644 --- a/src/widgets/star-toolbar.cpp +++ b/src/widgets/star-toolbar.cpp @@ -83,9 +83,9 @@ static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKlu bool modmade = false; Inkscape::Selection *selection = desktop->getSelection(); - GSList const *items = selection->itemList(); - for (; items != NULL; items = items->next) { - SPItem *item = reinterpret_cast<SPItem*>(items->data); + std::vector<SPItem*> itemlist=selection->itemList(); + for(std::vector<SPItem*>::const_iterator i=itemlist.begin();i!=itemlist.end();i++){ + SPItem *item = *i; if (SP_IS_STAR(item)) { Inkscape::XML::Node *repr = item->getRepr(); sp_repr_set_int(repr,"sodipodi:sides", @@ -128,9 +128,9 @@ static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKl bool modmade = false; Inkscape::Selection *selection = desktop->getSelection(); - GSList const *items = selection->itemList(); - for (; items != NULL; items = items->next) { - SPItem *item = reinterpret_cast<SPItem *>(items->data); + std::vector<SPItem*> itemlist=selection->itemList(); + for(std::vector<SPItem*>::const_iterator i=itemlist.begin();i!=itemlist.end();i++){ + SPItem *item = *i; if (SP_IS_STAR(item)) { Inkscape::XML::Node *repr = item->getRepr(); @@ -178,7 +178,6 @@ static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *d g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) ); Inkscape::Selection *selection = desktop->getSelection(); - GSList const *items = selection->itemList(); GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) ); bool modmade = false; @@ -186,8 +185,9 @@ static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *d gtk_action_set_sensitive( prop_action, !flat ); } - for (; items != NULL; items = items->next) { - SPItem *item = reinterpret_cast<SPItem *>(items->data); + std::vector<SPItem*> itemlist=selection->itemList(); + for(std::vector<SPItem*>::const_iterator i=itemlist.begin();i!=itemlist.end();i++){ + SPItem *item = *i; if (SP_IS_STAR(item)) { Inkscape::XML::Node *repr = item->getRepr(); repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" ); @@ -224,9 +224,9 @@ static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludg bool modmade = false; Inkscape::Selection *selection = desktop->getSelection(); - GSList const *items = selection->itemList(); - for (; items != NULL; items = items->next) { - SPItem *item = reinterpret_cast<SPItem*>(items->data); + std::vector<SPItem*> itemlist=selection->itemList(); + for(std::vector<SPItem*>::const_iterator i=itemlist.begin();i!=itemlist.end();i++){ + SPItem *item = *i; if (SP_IS_STAR(item)) { Inkscape::XML::Node *repr = item->getRepr(); sp_repr_set_svg_double(repr, "inkscape:rounded", @@ -264,9 +264,9 @@ static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKl bool modmade = false; Inkscape::Selection *selection = desktop->getSelection(); - GSList const *items = selection->itemList(); - for (; items != NULL; items = items->next) { - SPItem *item = reinterpret_cast<SPItem *>(items->data); + std::vector<SPItem*> itemlist=selection->itemList(); + for(std::vector<SPItem*>::const_iterator i=itemlist.begin();i!=itemlist.end();i++){ + SPItem *item = *i; if (SP_IS_STAR(item)) { Inkscape::XML::Node *repr = item->getRepr(); sp_repr_set_svg_double(repr, "inkscape:randomized", @@ -367,11 +367,9 @@ sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl) purge_repr_listener( tbl, tbl ); - for (GSList const *items = selection->itemList(); - items != NULL; - items = items->next) - { - SPItem* item = reinterpret_cast<SPItem *>(items->data); + std::vector<SPItem*> itemlist=selection->itemList(); + for(std::vector<SPItem*>::const_iterator i=itemlist.begin();i!=itemlist.end();i++){ + SPItem *item = *i; if (SP_IS_STAR(item)) { n_selected++; repr = item->getRepr(); diff --git a/src/widgets/stroke-marker-selector.h b/src/widgets/stroke-marker-selector.h index 27ac76d6f..a7ee788b8 100644 --- a/src/widgets/stroke-marker-selector.h +++ b/src/widgets/stroke-marker-selector.h @@ -10,14 +10,6 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif - -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include <gtkmm/box.h> #include <gtkmm/combobox.h> #include <gtkmm/liststore.h> diff --git a/src/widgets/stroke-style.cpp b/src/widgets/stroke-style.cpp index 5ca06a795..d05b3b994 100644 --- a/src/widgets/stroke-style.cpp +++ b/src/widgets/stroke-style.cpp @@ -450,7 +450,11 @@ StrokeStyle::makeRadioButton(Gtk::RadioButtonGroup &grp, */ void StrokeStyle::markerSelectCB(MarkerComboBox *marker_combo, StrokeStyle *spw, SPMarkerLoc const /*which*/) { - if (spw->update) { + bool markers_update = spw->startMarkerCombo->update() || + spw->midMarkerCombo->update() || + spw->endMarkerCombo->update(); + + if (spw->update || markers_update) { return; } @@ -475,9 +479,9 @@ void StrokeStyle::markerSelectCB(MarkerComboBox *marker_combo, StrokeStyle *spw, //spw->updateMarkerHist(which); Inkscape::Selection *selection = spw->desktop->getSelection(); - GSList const *items = selection->itemList(); - for (; items != NULL; items = items->next) { - SPItem *item = reinterpret_cast<SPItem *>(items->data); + std::vector<SPItem*> itemlist=selection->itemList(); + for(std::vector<SPItem*>::const_iterator i=itemlist.begin();i!=itemlist.end();i++){ + SPItem *item = *i; if (!SP_IS_SHAPE(item) || SP_IS_RECT(item)) { // can't set marker to rect, until it's converted to using <path> continue; } @@ -901,8 +905,8 @@ StrokeStyle::updateLine() if (!sel || sel->isEmpty()) return; - GSList const *objects = sel->itemList(); - SPObject * const object = SP_OBJECT(objects->data); + std::vector<SPItem*> const objects = sel->itemList(); + SPObject * const object = objects[0]; SPStyle * const style = object->style; /* Markers */ @@ -957,13 +961,12 @@ StrokeStyle::scaleLine() SPDocument *document = desktop->getDocument(); Inkscape::Selection *selection = desktop->getSelection(); - - GSList const *items = selection->itemList(); + std::vector<SPItem*> items=selection->itemList(); /* TODO: Create some standardized method */ SPCSSAttr *css = sp_repr_css_attr_new(); - if (items) { + if (!items.empty()) { #if WITH_GTKMM_3_0 double width_typed = (*widthAdj)->get_value(); double const miterlimit = (*miterLimitAdj)->get_value(); @@ -978,13 +981,13 @@ StrokeStyle::scaleLine() int ndash; dashSelector->get_dash(&ndash, &dash, &offset); - for (GSList const *i = items; i != NULL; i = i->next) { + for(std::vector<SPItem*>::const_iterator i=items.begin();i!=items.end();i++){ /* Set stroke width */ double width; if (unit->type == Inkscape::Util::UNIT_TYPE_LINEAR) { width = Inkscape::Util::Quantity::convert(width_typed, unit, "px"); } else { // percentage - gdouble old_w = SP_OBJECT(i->data)->style->stroke_width.computed; + gdouble old_w = (*i)->style->stroke_width.computed; width = old_w * width_typed / 100; } @@ -1003,7 +1006,7 @@ StrokeStyle::scaleLine() /* Set dash */ setScaledDash(css, ndash, dash, offset, width); - sp_desktop_apply_css_recursive (SP_OBJECT(i->data), css, true); + sp_desktop_apply_css_recursive ((*i), css, true); } g_free(dash); @@ -1144,7 +1147,7 @@ StrokeStyle::setCapButtons(Gtk::ToggleButton *active) * that marker. */ void -StrokeStyle::updateAllMarkers(GSList const *objects) +StrokeStyle::updateAllMarkers(std::vector<SPItem*> const &objects) { struct { MarkerComboBox *key; int loc; } const keyloc[] = { { startMarkerCombo, SP_MARKER_LOC_START }, @@ -1153,9 +1156,10 @@ StrokeStyle::updateAllMarkers(GSList const *objects) }; bool all_texts = true; - for (GSList *i = (GSList *) objects; i != NULL; i = i->next) { - if (!SP_IS_TEXT (i->data)) { + for(std::vector<SPItem*>::const_iterator i=objects.begin();i!=objects.end();i++){ + if (!SP_IS_TEXT (*i)) { all_texts = false; + break; } } @@ -1167,7 +1171,7 @@ StrokeStyle::updateAllMarkers(GSList const *objects) // We show markers of the first object in the list only // FIXME: use the first in the list that has the marker of each type, if any - SPObject *object = SP_OBJECT(objects->data); + SPObject *object = objects[0]; for (unsigned i = 0; i < G_N_ELEMENTS(keyloc); ++i) { // For all three marker types, diff --git a/src/widgets/stroke-style.h b/src/widgets/stroke-style.h index 83048cb76..2605e1acf 100644 --- a/src/widgets/stroke-style.h +++ b/src/widgets/stroke-style.h @@ -153,7 +153,7 @@ private: }; void updateLine(); - void updateAllMarkers(GSList const *objects); + void updateAllMarkers(std::vector<SPItem*> const &objects); void updateMarkerHist(SPMarkerLoc const which); void setDashSelectorFromStyle(SPDashSelector *dsel, SPStyle *style); void setJoinType (unsigned const jointype); diff --git a/src/widgets/swatch-selector.cpp b/src/widgets/swatch-selector.cpp index 7178ad072..6f2807255 100644 --- a/src/widgets/swatch-selector.cpp +++ b/src/widgets/swatch-selector.cpp @@ -5,11 +5,11 @@ #include "document-undo.h" #include "gradient-chemistry.h" #include "gradient-selector.h" -#include "sp-color-notebook.h" #include "sp-stop.h" #include "svg/css-ostringstream.h" #include "svg/svg-color.h" #include "verbs.h" +#include "ui/widget/color-notebook.h" #include "xml/node.h" namespace Inkscape @@ -20,8 +20,10 @@ namespace Widgets SwatchSelector::SwatchSelector() : Gtk::VBox(), _gsel(0), - _csel(0) + _updating_color(false) { + using Inkscape::UI::Widget::ColorNotebook; + GtkWidget *gsel = sp_gradient_selector_new(); _gsel = SP_GRADIENT_SELECTOR(gsel); g_object_set_data( G_OBJECT(gobj()), "base", this ); @@ -31,27 +33,18 @@ SwatchSelector::SwatchSelector() : pack_start(*Gtk::manage(Glib::wrap(gsel))); + Gtk::Widget *color_selector = Gtk::manage(new ColorNotebook(_selected_color)); + color_selector->show(); + pack_start(*color_selector); - GtkWidget *csel = sp_color_selector_new( SP_TYPE_COLOR_NOTEBOOK ); - _csel = SP_COLOR_SELECTOR(csel); - Gtk::Widget *wrappedCSel = Glib::wrap(csel); - wrappedCSel->show(); - //gtk_widget_show(csel); - - - GObject *obj = G_OBJECT(csel); - - g_signal_connect(obj, "grabbed", G_CALLBACK(_grabbedCb), this); - g_signal_connect(obj, "dragged", G_CALLBACK(_draggedCb), this); - g_signal_connect(obj, "released", G_CALLBACK(_releasedCb), this); - g_signal_connect(obj, "changed", G_CALLBACK(_changedCb), this); - - pack_start(*Gtk::manage(wrappedCSel)); + _selected_color.signal_grabbed.connect(sigc::mem_fun(this, &SwatchSelector::_grabbedCb)); + _selected_color.signal_dragged.connect(sigc::mem_fun(this, &SwatchSelector::_draggedCb)); + _selected_color.signal_released.connect(sigc::mem_fun(this, &SwatchSelector::_releasedCb)); + _selected_color.signal_changed.connect(sigc::mem_fun(this, &SwatchSelector::_changedCb)); } SwatchSelector::~SwatchSelector() { - _csel = 0; // dtor should be handled by Gtk::manage() _gsel = 0; } @@ -60,13 +53,13 @@ SPGradientSelector *SwatchSelector::getGradientSelector() return _gsel; } -void SwatchSelector::_grabbedCb(SPColorSelector * /*csel*/, void * /*data*/) +void SwatchSelector::_grabbedCb() { } -void SwatchSelector::_draggedCb(SPColorSelector * /*csel*/, void *data) +void SwatchSelector::_draggedCb() { - if (data) { + // if (data) { //SwatchSelector *swsel = reinterpret_cast<SwatchSelector*>(data); // TODO might have to block cycles @@ -92,50 +85,46 @@ void SwatchSelector::_draggedCb(SPColorSelector * /*csel*/, void *data) } } */ - } + // } } -void SwatchSelector::_releasedCb(SPColorSelector * /*csel*/, void * /*data*/) +void SwatchSelector::_releasedCb() { } -void SwatchSelector::_changedCb(SPColorSelector */*csel*/, void *data) +void SwatchSelector::_changedCb() { - if (data) { - SwatchSelector *swsel = reinterpret_cast<SwatchSelector*>(data); + if (_updating_color) { + return; + } + // TODO might have to block cycles + + if (_gsel && _gsel->getVector()) { + SPGradient *gradient = _gsel->getVector(); + SPGradient *ngr = sp_gradient_ensure_vector_normalized(gradient); + if (ngr != gradient) { + /* Our master gradient has changed */ + // TODO replace with proper - sp_gradient_vector_widget_load_gradient(GTK_WIDGET(swsel->_gsel), ngr); + } - // TODO might have to block cycles + ngr->ensureVector(); - if (swsel->_gsel && swsel->_gsel->getVector()) { - SPGradient *gradient = swsel->_gsel->getVector(); - SPGradient *ngr = sp_gradient_ensure_vector_normalized(gradient); - if (ngr != gradient) { - /* Our master gradient has changed */ - // TODO replace with proper - sp_gradient_vector_widget_load_gradient(GTK_WIDGET(swsel->_gsel), ngr); - } - ngr->ensureVector(); + SPStop* stop = ngr->getFirstStop(); + if (stop) { + SPColor color = _selected_color.color(); + gfloat alpha = _selected_color.alpha(); + guint32 rgb = color.toRGBA32( 0x00 ); + // TODO replace with generic shared code that also handles icc-color + Inkscape::CSSOStringStream os; + gchar c[64]; + sp_svg_write_color(c, sizeof(c), rgb); + os << "stop-color:" << c << ";stop-opacity:" << static_cast<gdouble>(alpha) <<";"; + stop->getRepr()->setAttribute("style", os.str().c_str()); - SPStop* stop = ngr->getFirstStop(); - if (stop) { - SPColor color; - float alpha = 0; - guint32 rgb = 0; - - swsel->_csel->base->getColorAlpha( color, alpha ); - rgb = color.toRGBA32( 0x00 ); - - // TODO replace with generic shared code that also handles icc-color - Inkscape::CSSOStringStream os; - gchar c[64]; - sp_svg_write_color(c, sizeof(c), rgb); - os << "stop-color:" << c << ";stop-opacity:" << static_cast<gdouble>(alpha) <<";"; - stop->getRepr()->setAttribute("style", os.str().c_str()); - - DocumentUndo::done(ngr->document, SP_VERB_CONTEXT_GRADIENT, - _("Change swatch color")); - } + DocumentUndo::done(ngr->document, SP_VERB_CONTEXT_GRADIENT, + _("Change swatch color")); } } } @@ -173,11 +162,10 @@ void SwatchSelector::setVector(SPDocument */*doc*/, SPGradient *vector) SPStop* stop = vector->getFirstStop(); guint32 const colorVal = stop->get_rgba32(); - _csel->base->setAlpha(SP_RGBA32_A_F(colorVal)); - SPColor color( SP_RGBA32_R_F(colorVal), SP_RGBA32_G_F(colorVal), SP_RGBA32_B_F(colorVal) ); - // set its color, from the stored array - _csel->base->setColor( color ); - gtk_widget_show_all( GTK_WIDGET(_csel) ); + _updating_color = true; + _selected_color.setValue(colorVal); + _updating_color = false; + // gtk_widget_show_all( GTK_WIDGET(_csel) ); } else { //gtk_widget_hide( GTK_WIDGET(_csel) ); } diff --git a/src/widgets/swatch-selector.h b/src/widgets/swatch-selector.h index c8c9983a6..16a8cfac8 100644 --- a/src/widgets/swatch-selector.h +++ b/src/widgets/swatch-selector.h @@ -1,15 +1,8 @@ #ifndef SEEN_SP_SWATCH_SELECTOR_H #define SEEN_SP_SWATCH_SELECTOR_H -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif - -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include <gtkmm/box.h> +#include "ui/selected-color.h" class SPDocument; class SPGradient; @@ -37,13 +30,14 @@ public: SPGradientSelector *getGradientSelector(); private: - static void _grabbedCb(SPColorSelector *csel, void *data); - static void _draggedCb(SPColorSelector *csel, void *data); - static void _releasedCb(SPColorSelector *csel, void *data); - static void _changedCb(SPColorSelector *csel, void *data); + void _grabbedCb(); + void _draggedCb(); + void _releasedCb(); + void _changedCb(); SPGradientSelector *_gsel; - SPColorSelector *_csel; + Inkscape::UI::SelectedColor _selected_color; + bool _updating_color; }; diff --git a/src/widgets/text-toolbar.cpp b/src/widgets/text-toolbar.cpp index 3d2e6eef8..7b22e4af7 100644 --- a/src/widgets/text-toolbar.cpp +++ b/src/widgets/text-toolbar.cpp @@ -162,11 +162,17 @@ static void sp_text_fontfamily_value_changed( Ink_ComboBoxEntry_Action *act, GOb fontlister->fill_css( css ); SPDesktop *desktop = SP_ACTIVE_DESKTOP; - sp_desktop_set_style (desktop, css, true, true); // Results in selection change called twice. + if( desktop->getSelection()->isEmpty() ) { + // Update default + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + prefs->mergeStyle("/tools/text/style", css); + } else { + // If there is a selection, update + sp_desktop_set_style (desktop, css, true, true); // Results in selection change called twice. + DocumentUndo::done(desktop->getDocument(), SP_VERB_CONTEXT_TEXT, + _("Text: Change font family")); + } sp_repr_css_attr_unref (css); - - DocumentUndo::done(desktop->getDocument(), SP_VERB_CONTEXT_TEXT, - _("Text: Change font family")); } // unfreeze @@ -366,9 +372,10 @@ static void sp_text_align_mode_changed( EgeSelectOneAction *act, GObject *tbl ) // move the x of all texts to preserve the same bbox Inkscape::Selection *selection = desktop->getSelection(); - for (GSList const *items = selection->itemList(); items != NULL; items = items->next) { - if (SP_IS_TEXT(SP_ITEM(items->data))) { - SPItem *item = SP_ITEM(items->data); + std::vector<SPItem*> itemlist=selection->itemList(); + for(std::vector<SPItem*>::const_iterator i=itemlist.begin();i!=itemlist.end();i++){ + if (SP_IS_TEXT(*i)) { + SPItem *item = *i; unsigned writing_mode = item->style->writing_mode.value; // below, variable names suggest horizontal move, but we check the writing direction @@ -517,11 +524,11 @@ static void sp_text_lineheight_value_changed( GtkAdjustment *adj, GObject *tbl ) // Until deprecated sodipodi:linespacing purged: Inkscape::Selection *selection = desktop->getSelection(); - GSList const *items = selection->itemList(); bool modmade = false; - for (; items != NULL; items = items->next) { - if (SP_IS_TEXT (items->data)) { - SP_OBJECT(items->data)->getRepr()->setAttribute("sodipodi:linespacing", sp_repr_css_property (css, "line-height", NULL)); + std::vector<SPItem*> itemlist=selection->itemList(); + for(std::vector<SPItem*>::const_iterator i=itemlist.begin();i!=itemlist.end();i++){ + if (SP_IS_TEXT (*i)) { + (*i)->getRepr()->setAttribute("sodipodi:linespacing", sp_repr_css_property (css, "line-height", NULL)); modmade = true; } } @@ -863,12 +870,11 @@ static void sp_text_toolbox_selection_changed(Inkscape::Selection */*selection*/ // Only flowed text can be justified, only normal text can be kerned... // Find out if we have flowed text now so we can use it several places gboolean isFlow = false; - for (GSList const *items = SP_ACTIVE_DESKTOP->getSelection()->itemList(); - items != NULL; - items = items->next) { + std::vector<SPItem*> itemlist=SP_ACTIVE_DESKTOP->getSelection()->itemList(); + for(std::vector<SPItem*>::const_iterator i=itemlist.begin();i!=itemlist.end();i++){ // const gchar* id = reinterpret_cast<SPItem *>(items->data)->getId(); // std::cout << " " << id << std::endl; - if( SP_IS_FLOWTEXT(SP_ITEM(items->data))) { + if( SP_IS_FLOWTEXT(*i)) { isFlow = true; // std::cout << " Found flowed text" << std::endl; break; @@ -1153,14 +1159,14 @@ static void sp_text_toolbox_select_cb( GtkEntry* entry, GtkEntryIconPosition /*p //std::cout << "text_toolbox_missing_font_cb: selecting: " << family << std::endl; // Get all items with matching font-family set (not inherited!). - GSList *selectList = NULL; + std::vector<SPItem*> selectList; SPDesktop *desktop = SP_ACTIVE_DESKTOP; SPDocument *document = desktop->getDocument(); - GSList *allList = get_all_items(NULL, document->getRoot(), desktop, false, false, true, NULL); - for (GSList *i = allList; i != NULL; i = i->next) { - - SPItem *item = SP_ITEM(i->data); + std::vector<SPItem*> x,y; + std::vector<SPItem*> allList = get_all_items(x, document->getRoot(), desktop, false, false, true, y); + for(std::vector<SPItem*>::const_reverse_iterator i=allList.rbegin();i!=allList.rend();i++){ + SPItem *item = *i; SPStyle *style = item->style; if (style) { @@ -1177,7 +1183,7 @@ static void sp_text_toolbox_select_cb( GtkEntry* entry, GtkEntryIconPosition /*p if (family_style.compare( family ) == 0 ) { //std::cout << " found: " << item->getId() << std::endl; - selectList = g_slist_prepend (selectList, item); + selectList.push_back(item); } } } diff --git a/src/widgets/toolbox.cpp b/src/widgets/toolbox.cpp index 5d52db6f2..d56b91f5e 100644 --- a/src/widgets/toolbox.cpp +++ b/src/widgets/toolbox.cpp @@ -29,10 +29,6 @@ # include "config.h" #endif -#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H -#include <glibmm/threads.h> -#endif - #include <gtkmm/box.h> #include <gtkmm/action.h> #include <gtkmm/actiongroup.h> diff --git a/src/xml/repr-util.cpp b/src/xml/repr-util.cpp index 7c5d2d6fc..f7a437163 100644 --- a/src/xml/repr-util.cpp +++ b/src/xml/repr-util.cpp @@ -310,6 +310,11 @@ int sp_repr_compare_position(Inkscape::XML::Node const *first, Inkscape::XML::No pjrm */ } +bool sp_repr_compare_position_bool(Inkscape::XML::Node const *first, Inkscape::XML::Node const *second){ + return sp_repr_compare_position(first, second)<0; +} + + /** * Find an element node using an unique attribute. * @@ -366,6 +371,34 @@ Inkscape::XML::Node *sp_repr_lookup_name( Inkscape::XML::Node *repr, gchar const return const_cast<Inkscape::XML::Node *>(found); } +std::vector<Inkscape::XML::Node const *> sp_repr_lookup_name_many( Inkscape::XML::Node const *repr, gchar const *name, gint maxdepth ) +{ + std::vector<Inkscape::XML::Node const *> nodes; + std::vector<Inkscape::XML::Node const *> found; + g_return_val_if_fail(repr != NULL, nodes); + g_return_val_if_fail(name != NULL, nodes); + + GQuark const quark = g_quark_from_string(name); + + if ( (GQuark)repr->code() == quark ) { + nodes.push_back(repr); + } + + if ( maxdepth != 0 ) { + // maxdepth == -1 means unlimited + if ( maxdepth == -1 ) { + maxdepth = 0; + } + + for (Inkscape::XML::Node const *child = repr->firstChild() ; child; child = child->next() ) { + found = sp_repr_lookup_name_many( child, name, maxdepth - 1); + nodes.insert(nodes.end(), found.begin(), found.end()); + } + } + + return nodes; +} + /** * Determine if the node is a 'title', 'desc' or 'metadata' element. */ diff --git a/src/xml/repr.h b/src/xml/repr.h index c3ba40e45..e84b89813 100644 --- a/src/xml/repr.h +++ b/src/xml/repr.h @@ -14,6 +14,7 @@ #ifndef SEEN_SP_REPR_H #define SEEN_SP_REPR_H +#include <vector> #include <glibmm/quark.h> #include "xml/node.h" @@ -120,7 +121,9 @@ unsigned sp_repr_set_svg_length(Inkscape::XML::Node *repr, char const *key, SVGL unsigned sp_repr_set_point(Inkscape::XML::Node *repr, char const *key, Geom::Point const & val); unsigned sp_repr_get_point(Inkscape::XML::Node *repr, char const *key, Geom::Point *val); +//c++-style comparison : returns (bool)(a<b) int sp_repr_compare_position(Inkscape::XML::Node const *first, Inkscape::XML::Node const *second); +bool sp_repr_compare_position_bool(Inkscape::XML::Node const *first, Inkscape::XML::Node const *second); // Searching /** @@ -142,6 +145,10 @@ Inkscape::XML::Node *sp_repr_lookup_name(Inkscape::XML::Node *repr, Inkscape::XML::Node const *sp_repr_lookup_name(Inkscape::XML::Node const *repr, char const *name, int maxdepth = -1); + +std::vector<Inkscape::XML::Node const *> sp_repr_lookup_name_many(Inkscape::XML::Node const *repr, + char const *name, + int maxdepth = -1); Inkscape::XML::Node *sp_repr_lookup_child(Inkscape::XML::Node *repr, char const *key, |
