diff options
Diffstat (limited to 'src/gradient-chemistry.cpp')
| -rw-r--r-- | src/gradient-chemistry.cpp | 201 |
1 files changed, 117 insertions, 84 deletions
diff --git a/src/gradient-chemistry.cpp b/src/gradient-chemistry.cpp index edeb523d7..05f594f86 100644 --- a/src/gradient-chemistry.cpp +++ b/src/gradient-chemistry.cpp @@ -24,7 +24,6 @@ #include <2geom/bezier-curve.h> #include <2geom/crossing.h> #include <2geom/line.h> -#include <2geom/angle.h> #include "style.h" #include "document-private.h" @@ -38,21 +37,18 @@ #include <glibmm/i18n.h> #include "sp-gradient-reference.h" -#include "sp-gradient-vector.h" #include "sp-linear-gradient.h" #include "sp-radial-gradient.h" -#include "sp-mesh.h" +#include "sp-mesh-gradient.h" #include "sp-stop.h" #include "gradient-drag.h" #include "gradient-chemistry.h" #include "sp-text.h" #include "sp-tspan.h" -#include "xml/repr.h" #include "svg/svg.h" #include "svg/svg-color.h" #include "svg/css-ostringstream.h" -#include "preferences.h" #define noSP_GR_VERBOSE @@ -80,9 +76,14 @@ std::vector<PaintTarget> const &allPaintTargets() // "vector" is a gradient that has stops but not position coords. It can be referenced by one or // more privates. Objects should not refer to it directly. It has no radial/linear distinction. // -// "private" is a gradient that has no stops but has position coords (e.g. center, radius etc for a -// radial). It references a vector for the actual colors. Each private is only used by one -// object. It is either linear or radial. +// "array" is a gradient that has mesh rows and patches. It may or may not have "x" and "y" attributes. +// An array does have spacial information so it cannot be normalized like a "vector". +// +// "shared" is either a "vector" or "array" that is shared between multiple objects. +// +// "private" is a gradient that is not shared. A private linear or radial gradient has no stops but +// has position coords (e.g. center, radius etc for a radial); it references a "vector" for the +// actual colors. A mesh may or may not reference an array. Each private is only used by one object. static void sp_gradient_repr_set_link(Inkscape::XML::Node *repr, SPGradient *gr); @@ -93,6 +94,7 @@ SPGradient *sp_gradient_ensure_vector_normalized(SPGradient *gr) #endif g_return_val_if_fail(gr != NULL, NULL); g_return_val_if_fail(SP_IS_GRADIENT(gr), NULL); + g_return_val_if_fail(!SP_IS_MESHGRADIENT(gr), NULL); /* If we are already normalized vector, just return */ if (gr->state == SP_GRADIENT_STATE_VECTOR) return gr; @@ -126,19 +128,19 @@ SPGradient *sp_gradient_ensure_vector_normalized(SPGradient *gr) } /** - * Creates new private gradient for the given vector + * Creates new private gradient for the given shared gradient. */ -static SPGradient *sp_gradient_get_private_normalized(SPDocument *document, SPGradient *vector, SPGradientType type) +static SPGradient *sp_gradient_get_private_normalized(SPDocument *document, SPGradient *shared, SPGradientType type) { #ifdef SP_GR_VERBOSE - g_message("sp_gradient_get_private_normalized(%p, %p, %d)", document, vector, type); + g_message("sp_gradient_get_private_normalized(%p, %p, %d)", document, shared, type); #endif g_return_val_if_fail(document != NULL, NULL); - g_return_val_if_fail(vector != NULL, NULL); - g_return_val_if_fail(SP_IS_GRADIENT(vector), NULL); - g_return_val_if_fail(vector->hasStops(), NULL); + g_return_val_if_fail(shared != NULL, NULL); + g_return_val_if_fail(SP_IS_GRADIENT(shared), NULL); + g_return_val_if_fail(shared->hasStops() || shared->hasPatches(), NULL); SPDefs *defs = document->getDefs(); @@ -150,16 +152,14 @@ static SPGradient *sp_gradient_get_private_normalized(SPDocument *document, SPGr } else if(type == SP_GRADIENT_TYPE_RADIAL) { repr = xml_doc->createElement("svg:radialGradient"); } else { - // Rows/patches added in sp_gradient_reset_to_userspace for new meshes. - repr = xml_doc->createElement("svg:mesh"); + repr = xml_doc->createElement("svg:meshgradient"); } // privates are garbage-collectable repr->setAttribute("inkscape:collect", "always"); - // link to vector - // MESH FIXME: Meshes don't used vector... but meshes simulating gradient across/along path might. - sp_gradient_repr_set_link(repr, vector); + // link to shared + sp_gradient_repr_set_link(repr, shared); /* Append the new private gradient to defs */ defs->getRepr()->appendChild(repr); @@ -199,8 +199,8 @@ static guint count_gradient_hrefs(SPObject *o, SPGradient *gr) i ++; } - for ( SPObject *child = o->firstChild(); child; child = child->getNext() ) { - i += count_gradient_hrefs(child, gr); + for (auto& child: o->children) { + i += count_gradient_hrefs(&child, gr); } return i; @@ -208,21 +208,21 @@ static guint count_gradient_hrefs(SPObject *o, SPGradient *gr) /** - * If gr has other users, create a new private; also check if gr links to vector, relink if not + * If gr has other users, create a new shared; also check if gr links to shared, relink if not */ -static SPGradient *sp_gradient_fork_private_if_necessary(SPGradient *gr, SPGradient *vector, +static SPGradient *sp_gradient_fork_private_if_necessary(SPGradient *gr, SPGradient *shared, SPGradientType type, SPObject *o) { #ifdef SP_GR_VERBOSE - g_message("sp_gradient_fork_private_if_necessary(%p, %p, %d, %p)", gr, vector, type, o); + g_message("sp_gradient_fork_private_if_necessary(%p, %p, %d, %p)", gr, shared, type, o); #endif g_return_val_if_fail(gr != NULL, NULL); g_return_val_if_fail(SP_IS_GRADIENT(gr), NULL); - // Orphaned gradient, no vector with stops at the end of the line; this used to be an assert - // but i think we should not abort on this - maybe just write a validity warning into some sort - // of log - if ( !vector || !vector->hasStops() ) { + // Orphaned gradient, no shared with stops or patches at the end of the line; this used to be + // an assert + if ( !shared || !(shared->hasStops() || shared->hasPatches()) ) { + std::cerr << "sp_gradient_fork_private_if_necessary: Orphaned gradient" << std::endl; return (gr); } @@ -236,11 +236,11 @@ static SPGradient *sp_gradient_fork_private_if_necessary(SPGradient *gr, SPGradi // Check the number of uses of the gradient within this object; // if we are private and there are no other users, - if (!vector->isSwatch() && (gr->hrefcount <= count_gradient_hrefs(user, gr))) { - // check vector - if ( gr != vector && gr->ref->getObject() != vector ) { - /* our href is not the vector, and vector is different from gr; relink */ - sp_gradient_repr_set_link(gr->getRepr(), vector); + if (!shared->isSwatch() && (gr->hrefcount <= count_gradient_hrefs(user, gr))) { + // check shared + if ( gr != shared && gr->ref->getObject() != shared ) { + /* our href is not the shared, and shared is different from gr; relink */ + sp_gradient_repr_set_link(gr->getRepr(), shared); } return gr; } @@ -249,35 +249,49 @@ static SPGradient *sp_gradient_fork_private_if_necessary(SPGradient *gr, SPGradi SPObject *defs = doc->getDefs(); if ((gr->hasStops()) || + (gr->hasPatches()) || (gr->state != SP_GRADIENT_STATE_UNKNOWN) || (gr->parent != SP_OBJECT(defs)) || (gr->hrefcount > 1)) { - // we have to clone a fresh new private gradient for the given vector + + // we have to clone a fresh new private gradient for the given shared // create an empty one - SPGradient *gr_new = sp_gradient_get_private_normalized(doc, vector, type); + SPGradient *gr_new = sp_gradient_get_private_normalized(doc, shared, type); // copy all the attributes to it Inkscape::XML::Node *repr_new = gr_new->getRepr(); Inkscape::XML::Node *repr = gr->getRepr(); repr_new->setAttribute("gradientUnits", repr->attribute("gradientUnits")); repr_new->setAttribute("gradientTransform", repr->attribute("gradientTransform")); - repr_new->setAttribute("spreadMethod", repr->attribute("spreadMethod")); if (SP_IS_RADIALGRADIENT(gr)) { repr_new->setAttribute("cx", repr->attribute("cx")); repr_new->setAttribute("cy", repr->attribute("cy")); repr_new->setAttribute("fx", repr->attribute("fx")); repr_new->setAttribute("fy", repr->attribute("fy")); - repr_new->setAttribute("r", repr->attribute("r")); + repr_new->setAttribute("r", repr->attribute("r" )); + repr_new->setAttribute("fr", repr->attribute("fr")); + repr_new->setAttribute("spreadMethod", repr->attribute("spreadMethod")); } else if (SP_IS_LINEARGRADIENT(gr)) { repr_new->setAttribute("x1", repr->attribute("x1")); repr_new->setAttribute("y1", repr->attribute("y1")); repr_new->setAttribute("x2", repr->attribute("x2")); repr_new->setAttribute("y2", repr->attribute("y2")); - } else { - std::cout << "sp_gradient_fork_private_if_necessary: mesh not implemented" << std::endl; + repr_new->setAttribute("spreadMethod", repr->attribute("spreadMethod")); + } else { // Mesh + repr_new->setAttribute("x", repr->attribute("x")); + repr_new->setAttribute("y", repr->attribute("y")); + repr_new->setAttribute("type", repr->attribute("type")); + + // We probably want a completely separate mesh gradient so + // copy the children and unset the link to the shared. + for ( Inkscape::XML::Node *child = repr->firstChild() ; child ; child = child->next() ) { + Inkscape::XML::Node *copy = child->duplicate(doc->getReprDoc()); + repr_new->appendChild( copy ); + Inkscape::GC::release( copy ); + } + sp_gradient_repr_set_link(repr_new, NULL); } - return gr_new; } else { return gr; @@ -413,7 +427,9 @@ SPGradient *sp_gradient_reset_to_userspace(SPGradient *gr, SPItem *item) // IN SPMeshNodeArray::create() //sp_repr_set_svg_double(repr, "x", bbox->min()[Geom::X]); //sp_repr_set_svg_double(repr, "y", bbox->min()[Geom::Y]); - SPMesh* mg = SP_MESH( gr ); + + // We don't create a shared array gradient. + SPMeshGradient* mg = SP_MESHGRADIENT( gr ); mg->array.create( mg, item, bbox ); } @@ -439,8 +455,13 @@ SPGradient *sp_gradient_convert_to_userspace(SPGradient *gr, SPItem *item, gchar } // First, fork it if it is shared - gr = sp_gradient_fork_private_if_necessary(gr, gr->getVector(), - SP_IS_RADIALGRADIENT(gr) ? SP_GRADIENT_TYPE_RADIAL : SP_GRADIENT_TYPE_LINEAR, item); + if (SP_IS_LINEARGRADIENT(gr)) { + gr = sp_gradient_fork_private_if_necessary(gr, gr->getVector(), SP_GRADIENT_TYPE_LINEAR, item); + } else if (SP_IS_RADIALGRADIENT(gr)) { + gr = sp_gradient_fork_private_if_necessary(gr, gr->getVector(), SP_GRADIENT_TYPE_RADIAL, item); + } else { + gr = sp_gradient_fork_private_if_necessary(gr, gr->getArray(), SP_GRADIENT_TYPE_MESH, item); + } if (gr->getUnits() == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) { @@ -494,7 +515,24 @@ SPGradient *sp_gradient_convert_to_userspace(SPGradient *gr, SPItem *item, gchar // as to cancel it out when it's applied to the gradient during rendering Geom::Affine point_convert = bbox2user * skew.inverse(); - if (SP_IS_RADIALGRADIENT(gr)) { + if (SP_IS_LINEARGRADIENT(gr)) { + SPLinearGradient *lg = SP_LINEARGRADIENT(gr); + + Geom::Point p1_b = Geom::Point(lg->x1.computed, lg->y1.computed); + Geom::Point p2_b = Geom::Point(lg->x2.computed, lg->y2.computed); + + Geom::Point p1_u = p1_b * point_convert; + Geom::Point p2_u = p2_b * point_convert; + + sp_repr_set_svg_double(repr, "x1", p1_u[Geom::X]); + sp_repr_set_svg_double(repr, "y1", p1_u[Geom::Y]); + sp_repr_set_svg_double(repr, "x2", p2_u[Geom::X]); + sp_repr_set_svg_double(repr, "y2", p2_u[Geom::Y]); + + // set the gradientUnits + repr->setAttribute("gradientUnits", "userSpaceOnUse"); + + } else if (SP_IS_RADIALGRADIENT(gr)) { SPRadialGradient *rg = SP_RADIALGRADIENT(gr); // original points in the bbox coords @@ -513,23 +551,12 @@ SPGradient *sp_gradient_convert_to_userspace(SPGradient *gr, SPItem *item, gchar sp_repr_set_svg_double(repr, "fy", f_u[Geom::Y]); sp_repr_set_svg_double(repr, "r", r_u); - } else { - SPLinearGradient *lg = SP_LINEARGRADIENT(gr); - - Geom::Point p1_b = Geom::Point(lg->x1.computed, lg->y1.computed); - Geom::Point p2_b = Geom::Point(lg->x2.computed, lg->y2.computed); - - Geom::Point p1_u = p1_b * point_convert; - Geom::Point p2_u = p2_b * point_convert; + // set the gradientUnits + repr->setAttribute("gradientUnits", "userSpaceOnUse"); - sp_repr_set_svg_double(repr, "x1", p1_u[Geom::X]); - sp_repr_set_svg_double(repr, "y1", p1_u[Geom::Y]); - sp_repr_set_svg_double(repr, "x2", p2_u[Geom::X]); - sp_repr_set_svg_double(repr, "y2", p2_u[Geom::Y]); + } else { + std::cerr << "sp_gradient_convert_to_userspace: Conversion of mesh to userspace not implemented" << std::endl; } - - // set the gradientUnits - repr->setAttribute("gradientUnits", "userSpaceOnUse"); } // apply the gradient to the item (may be necessary if we forked it); not recursive @@ -758,10 +785,10 @@ guint32 sp_item_gradient_stop_query_style(SPItem *item, GrPointType point_type, break; } return 0; - } else if (SP_IS_MESH(gradient)) { + } else if (SP_IS_MESHGRADIENT(gradient)) { // Mesh gradient - SPMesh *mg = SP_MESH(gradient); + SPMeshGradient *mg = SP_MESHGRADIENT(gradient); switch (point_type) { case POINT_MG_CORNER: { @@ -859,12 +886,13 @@ void sp_item_gradient_stop_set_style(SPItem *item, GrPointType point_type, guint } else { // Mesh gradient - SPMesh *mg = SP_MESH(gradient); + SPMeshGradient *mg = SP_MESHGRADIENT(gradient); bool changed = false; switch (point_type) { case POINT_MG_CORNER: { + // Update mesh array (which is not updated automatically when stop is changed?) gchar const* color_str = sp_repr_css_property( stop, "stop-color", NULL ); if( color_str ) { SPColor color( 0 ); @@ -884,9 +912,14 @@ void sp_item_gradient_stop_set_style(SPItem *item, GrPointType point_type, guint mg->array.corners[ point_i ]->opacity = opacity; changed = true; } + // Update stop if( changed ) { - gradient->requestModified(SP_OBJECT_MODIFIED_FLAG); - mg->array.write( mg ); + SPStop *stopi = mg->array.corners[ point_i ]->stop; + if (stopi) { + sp_repr_css_change(stopi->getRepr(), stop, "style"); + } else { + std::cerr << "sp_item_gradient_stop_set_style: null stopi" << std::endl; + } } break; } @@ -926,11 +959,11 @@ void sp_item_gradient_reverse_vector(SPItem *item, Inkscape::PaintTarget fill_or GSList *child_objects = NULL; std::vector<double> offsets; double offset; - for ( SPObject *child = vector->firstChild(); child; child = child->getNext()) { - child_reprs = g_slist_prepend (child_reprs, child->getRepr()); - child_objects = g_slist_prepend (child_objects, child); + for (auto& child: vector->children) { + child_reprs = g_slist_prepend (child_reprs, child.getRepr()); + child_objects = g_slist_prepend (child_objects, &child); offset=0; - sp_repr_get_double(child->getRepr(), "offset", &offset); + sp_repr_get_double(child.getRepr(), "offset", &offset); offsets.push_back(offset); } @@ -979,9 +1012,9 @@ void sp_item_gradient_invert_vector_color(SPItem *item, Inkscape::PaintTarget fi sp_gradient_repr_set_link(gradient->getRepr(), vector); } - for ( SPObject *child = vector->firstChild(); child; child = child->getNext()) { - if (SP_IS_STOP(child)) { - guint32 color = SP_STOP(child)->get_rgba32(); + for (auto& child: vector->children) { + if (SP_IS_STOP(&child)) { + guint32 color = SP_STOP(&child)->get_rgba32(); //g_message("Stop color %d", color); gchar c[64]; sp_svg_write_color (c, sizeof(c), @@ -994,7 +1027,7 @@ void sp_item_gradient_invert_vector_color(SPItem *item, Inkscape::PaintTarget fi ); SPCSSAttr *css = sp_repr_css_attr_new (); sp_repr_css_set_property (css, "stop-color", c); - sp_repr_css_change(child->getRepr(), css, "style"); + sp_repr_css_change(child.getRepr(), css, "style"); sp_repr_css_attr_unref (css); } } @@ -1209,8 +1242,8 @@ void sp_item_gradient_set_coords(SPItem *item, GrPointType point_type, guint poi gradient->requestModified(SP_OBJECT_MODIFIED_FLAG); } } - } else if (SP_IS_MESH(gradient)) { - SPMesh *mg = SP_MESH(gradient); + } else if (SP_IS_MESHGRADIENT(gradient)) { + SPMeshGradient *mg = SP_MESHGRADIENT(gradient); //Geom::Affine new_transform; //bool transform_set = false; @@ -1240,7 +1273,7 @@ void sp_item_gradient_set_coords(SPItem *item, GrPointType point_type, guint poi } if( write_repr ) { //std::cout << "Write mesh repr" << std::endl; - sp_mesh_repr_write( mg ); + mg->array.write( mg ); } } @@ -1274,10 +1307,10 @@ in desktop coordinates. */ Geom::Point getGradientCoords(SPItem *item, GrPointType point_type, guint point_i, Inkscape::PaintTarget fill_or_stroke) { + SPGradient *gradient = getGradient(item, fill_or_stroke); #ifdef SP_GR_VERBOSE - g_message("getGradientCoords(%p, %d, %d, %d)", item, point_type, point_i, fill_or_stroke); + g_message("getGradientCoords(%p, %d, %d, %d, %p)", item, point_type, point_i, fill_or_stroke, gradient); #endif - SPGradient *gradient = getGradient(item, fill_or_stroke); Geom::Point p (0, 0); @@ -1346,8 +1379,8 @@ Geom::Point getGradientCoords(SPItem *item, GrPointType point_type, guint point_ g_warning( "Bad radial gradient handle type" ); break; } - } else if (SP_IS_MESH(gradient)) { - SPMesh *mg = SP_MESH(gradient); + } else if (SP_IS_MESHGRADIENT(gradient)) { + SPMeshGradient *mg = SP_MESHGRADIENT(gradient); switch (point_type) { case POINT_MG_CORNER: @@ -1550,7 +1583,7 @@ SPGradient *sp_gradient_vector_for_object( SPDocument *const doc, SPDesktop *con SPIPaint const &paint = ( (fill_or_stroke == Inkscape::FOR_FILL) ? style.fill : style.stroke ); if (paint.isPaintserver()) { SPObject *server = (fill_or_stroke == Inkscape::FOR_FILL) ? o->style->getFillPaintServer() : o->style->getStrokePaintServer(); - if ( SP_IS_GRADIENT(server) ) { + if ( SP_IS_LINEARGRADIENT(server) || SP_IS_RADIALGRADIENT(server) ) { return SP_GRADIENT(server)->getVector(true); } else { color = sp_desktop_get_color(desktop, (fill_or_stroke == Inkscape::FOR_FILL)); @@ -1570,8 +1603,8 @@ void sp_gradient_invert_selected_gradients(SPDesktop *desktop, Inkscape::PaintTa { Inkscape::Selection *selection = desktop->getSelection(); - const std::vector<SPItem*> list=selection->itemList(); - for (std::vector<SPItem*>::const_iterator i = list.begin(); i != list.end(); ++i) { + auto list= selection->items(); + for (auto i = list.begin(); i != list.end(); ++i) { sp_item_gradient_invert_vector_color(*i, fill_or_stroke); } @@ -1595,8 +1628,8 @@ void sp_gradient_reverse_selected_gradients(SPDesktop *desktop) if (drag && !drag->selected.empty()) { drag->selected_reverse_vector(); } else { // If no drag or no dragger selected, act on selection (both fill and stroke gradients) - const std::vector<SPItem*> list=selection->itemList(); - for (std::vector<SPItem*>::const_iterator i = list.begin(); i != list.end(); ++i) { + auto list= selection->items(); + for (auto 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); } |
