Code

remove no longer relevant comment
[inkscape.git] / src / gradient-chemistry.cpp
1 #define __SP_GRADIENT_CHEMISTRY_C__
3 /*
4  * Various utility methods for gradients
5  *
6  * Authors:
7  *   Lauris Kaplinski <lauris@kaplinski.com>
8  *   bulia byak
9  *   Johan Engelen <j.b.c.engelen@ewi.utwente.nl>
10  *
11  * Copyright (C) 2007 Johan Engelen
12  * Copyright (C) 2001-2005 authors
13  * Copyright (C) 2001 Ximian, Inc.
14  *
15  * Released under GNU GPL, read the file 'COPYING' for more information
16  */
19 #include "style.h"
20 #include "document-private.h"
21 #include "desktop-style.h"
23 #include "sp-gradient-reference.h"
24 #include "sp-gradient-vector.h"
25 #include "sp-linear-gradient.h"
26 #include "sp-radial-gradient.h"
27 #include "sp-stop.h"
28 #include "widgets/gradient-vector.h"
30 #include "sp-text.h"
31 #include "sp-tspan.h"
32 #include <libnr/nr-matrix-fns.h>
33 #include "xml/repr.h"
34 #include "svg/svg.h"
35 #include "svg/svg-color.h"
38 // Terminology:
39 //
40 // "vector" is a gradient that has stops but not position coords. It can be referenced by one or
41 // more privates. Objects should not refer to it directly. It has no radial/linear distinction.
42 //
43 // "private" is a gradient that has no stops but has position coords (e.g. center, radius etc for a
44 // radial). It references a vector for the actual colors. Each private is only used by one
45 // object. It is either linear or radial.
47 static void sp_gradient_repr_set_link(Inkscape::XML::Node *repr, SPGradient *gr);
49 SPGradient *
50 sp_gradient_ensure_vector_normalized(SPGradient *gr)
51 {
52     g_return_val_if_fail(gr != NULL, NULL);
53     g_return_val_if_fail(SP_IS_GRADIENT(gr), NULL);
55     /* If we are already normalized vector, just return */
56     if (gr->state == SP_GRADIENT_STATE_VECTOR) return gr;
57     /* Fail, if we have wrong state set */
58     if (gr->state != SP_GRADIENT_STATE_UNKNOWN) {
59         g_warning("file %s: line %d: Cannot normalize private gradient to vector (%s)", __FILE__, __LINE__, SP_OBJECT_ID(gr));
60         return NULL;
61     }
63     /* First make sure we have vector directly defined (i.e. gr has its own stops) */
64     if (!gr->has_stops) {
65         /* We do not have stops ourselves, so flatten stops as well */
66         sp_gradient_ensure_vector(gr);
67         g_assert(gr->vector.built);
68         // this adds stops from gr->vector as children to gr
69         sp_gradient_repr_write_vector (gr);
70     }
72     /* If gr hrefs some other gradient, remove the href */
73     if (gr->ref->getObject()) {
74         /* We are hrefing someone, so require flattening */
75         SP_OBJECT(gr)->updateRepr(((SPObject *) gr)->repr, SP_OBJECT_WRITE_EXT | SP_OBJECT_WRITE_ALL);
76         sp_gradient_repr_set_link(SP_OBJECT_REPR(gr), NULL);
77     }
79     /* Everything is OK, set state flag */
80     gr->state = SP_GRADIENT_STATE_VECTOR;
81     return gr;
82 }
84 /**
85  * Creates new private gradient for the given vector
86  */
88 static SPGradient *
89 sp_gradient_get_private_normalized(SPDocument *document, SPGradient *vector, SPGradientType type)
90 {
91     g_return_val_if_fail(document != NULL, NULL);
92     g_return_val_if_fail(vector != NULL, NULL);
93     g_return_val_if_fail(SP_IS_GRADIENT(vector), NULL);
94     g_return_val_if_fail(SP_GRADIENT_HAS_STOPS(vector), NULL);
96     SPDefs *defs = (SPDefs *) SP_DOCUMENT_DEFS(document);
98     Inkscape::XML::Document *xml_doc = sp_document_repr_doc(document);
99     // create a new private gradient of the requested type
100     Inkscape::XML::Node *repr;
101     if (type == SP_GRADIENT_TYPE_LINEAR) {
102         repr = xml_doc->createElement("svg:linearGradient");
103     } else {
104         repr = xml_doc->createElement("svg:radialGradient");
105     }
107     // privates are garbage-collectable
108     repr->setAttribute("inkscape:collect", "always");
110     // link to vector
111     sp_gradient_repr_set_link(repr, vector);
113     /* Append the new private gradient to defs */
114     SP_OBJECT_REPR(defs)->appendChild(repr);
115     Inkscape::GC::release(repr);
117     // get corresponding object
118     SPGradient *gr = (SPGradient *) document->getObjectByRepr(repr);
119     g_assert(gr != NULL);
120     g_assert(SP_IS_GRADIENT(gr));
122     return gr;
125 /**
126 Count how many times gr is used by the styles of o and its descendants
127 */
128 guint
129 count_gradient_hrefs(SPObject *o, SPGradient *gr)
131     if (!o)
132         return 1;
134     guint i = 0;
136     SPStyle *style = SP_OBJECT_STYLE(o);
137     if (style
138         && style->fill.type == SP_PAINT_TYPE_PAINTSERVER
139         && SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style))
140         && SP_GRADIENT(SP_STYLE_FILL_SERVER(style)) == gr)
141     {
142         i ++;
143     }
144     if (style
145         && style->stroke.type == SP_PAINT_TYPE_PAINTSERVER
146         && SP_IS_GRADIENT(SP_STYLE_STROKE_SERVER(style))
147         && SP_GRADIENT(SP_STYLE_STROKE_SERVER(style)) == gr)
148     {
149         i ++;
150     }
152     for (SPObject *child = sp_object_first_child(o);
153          child != NULL; child = SP_OBJECT_NEXT(child)) {
154         i += count_gradient_hrefs(child, gr);
155     }
157     return i;
161 /**
162  * If gr has other users, create a new private; also check if gr links to vector, relink if not
163  */
164 SPGradient *
165 sp_gradient_fork_private_if_necessary(SPGradient *gr, SPGradient *vector,
166                                       SPGradientType type, SPObject *o)
168     g_return_val_if_fail(gr != NULL, NULL);
169     g_return_val_if_fail(SP_IS_GRADIENT(gr), NULL);
171     // Orphaned gradient, no vector with stops at the end of the line; this used to be an assert
172     // but i think we should not abort on this - maybe just write a validity warning into some sort
173     // of log
174     if (!vector || !SP_GRADIENT_HAS_STOPS(vector))
175         return (gr);
177     // user is the object that uses this gradient; normally it's item but for tspans, we
178     // check its ancestor text so that tspans don't get different gradients from their
179     // texts.
180     SPObject *user = o;
181     while (SP_IS_TSPAN(user)) {
182         user = SP_OBJECT_PARENT(user);
183     }
185     // Check the number of uses of the gradient within this object;
186     // if we are private and there are no other users,
187     if (SP_OBJECT_HREFCOUNT(gr) <= count_gradient_hrefs(user, gr)) {
188         // check vector
189         if ( gr != vector && gr->ref->getObject() != vector ) {
190             /* our href is not the vector, and vector is different from gr; relink */
191             sp_gradient_repr_set_link(SP_OBJECT_REPR(gr), vector);
192         }
193         return gr;
194     }
196     SPDocument *doc = SP_OBJECT_DOCUMENT(gr);
197     SPObject *defs = SP_DOCUMENT_DEFS(doc);
199     if ((gr->has_stops) ||
200         (gr->state != SP_GRADIENT_STATE_UNKNOWN) ||
201         (SP_OBJECT_PARENT(gr) != SP_OBJECT(defs)) ||
202         (SP_OBJECT_HREFCOUNT(gr) > 1)) {
203         // we have to clone a fresh new private gradient for the given vector
205         // create an empty one
206         SPGradient *gr_new = sp_gradient_get_private_normalized(doc, vector, type);
208         // copy all the attributes to it
209         Inkscape::XML::Node *repr_new = SP_OBJECT_REPR(gr_new);
210         Inkscape::XML::Node *repr = SP_OBJECT_REPR(gr);
211         repr_new->setAttribute("gradientUnits", repr->attribute("gradientUnits"));
212         repr_new->setAttribute("gradientTransform", repr->attribute("gradientTransform"));
213         repr_new->setAttribute("spreadMethod", repr->attribute("spreadMethod"));
214         if (SP_IS_RADIALGRADIENT(gr)) {
215             repr_new->setAttribute("cx", repr->attribute("cx"));
216             repr_new->setAttribute("cy", repr->attribute("cy"));
217             repr_new->setAttribute("fx", repr->attribute("fx"));
218             repr_new->setAttribute("fy", repr->attribute("fy"));
219             repr_new->setAttribute("r", repr->attribute("r"));
220         } else {
221             repr_new->setAttribute("x1", repr->attribute("x1"));
222             repr_new->setAttribute("y1", repr->attribute("y1"));
223             repr_new->setAttribute("x2", repr->attribute("x2"));
224             repr_new->setAttribute("y2", repr->attribute("y2"));
225         }
227         return gr_new;
228     } else {
229         return gr;
230     }
233 SPGradient *
234 sp_gradient_fork_vector_if_necessary (SPGradient *gr)
236     if (SP_OBJECT_HREFCOUNT(gr) > 1) {
237         SPDocument *doc = SP_OBJECT_DOCUMENT(gr);
238         Inkscape::XML::Document *xml_doc = sp_document_repr_doc(doc);
240         Inkscape::XML::Node *repr = SP_OBJECT_REPR (gr)->duplicate(xml_doc);
241         SP_OBJECT_REPR (SP_DOCUMENT_DEFS (doc))->addChild(repr, NULL);
242         SPGradient *gr_new = (SPGradient *) doc->getObjectByRepr(repr);
243         gr_new = sp_gradient_ensure_vector_normalized (gr_new);
244         Inkscape::GC::release(repr);
245         return gr_new;
246     }
247     return gr;
250 /**
251  *  Obtain the vector from the gradient. A forked vector will be created and linked to this gradient if another gradient uses it.
252  */
253 SPGradient *
254 sp_gradient_get_forked_vector_if_necessary(SPGradient *gradient, bool force_vector)
256     SPGradient *vector = sp_gradient_get_vector (gradient, force_vector);
257     vector = sp_gradient_fork_vector_if_necessary (vector);
258     if ( gradient != vector && gradient->ref->getObject() != vector ) {
259         sp_gradient_repr_set_link(SP_OBJECT_REPR(gradient), vector);
260     }
261     return vector;
265 /**
266  * Convert an item's gradient to userspace _without_ preserving coords, setting them to defaults
267  * instead. No forking or reapplying is done because this is only called for newly created privates.
268  * @return The new gradient.
269  */
270 SPGradient *
271 sp_gradient_reset_to_userspace (SPGradient *gr, SPItem *item)
273     Inkscape::XML::Node *repr = SP_OBJECT_REPR(gr);
275     // calculate the bbox of the item
276     sp_document_ensure_up_to_date(SP_OBJECT_DOCUMENT(item));
277     NR::Maybe<NR::Rect> bbox = item->getBounds(NR::identity()); // we need "true" bbox without item_i2d_affine
279     g_assert( bbox && !bbox->isEmpty() );
280     NR::Coord const width = bbox->dimensions()[NR::X];
281     NR::Coord const height = bbox->dimensions()[NR::Y];
283     NR::Point const center = bbox->midpoint();
285     if (SP_IS_RADIALGRADIENT(gr)) {
286         sp_repr_set_svg_double(repr, "cx", center[NR::X]);
287         sp_repr_set_svg_double(repr, "cy", center[NR::Y]);
288         sp_repr_set_svg_double(repr, "fx", center[NR::X]);
289         sp_repr_set_svg_double(repr, "fy", center[NR::Y]);
290         sp_repr_set_svg_double(repr, "r", width/2);
292         // we want it to be elliptic, not circular
293         NR::Matrix squeeze = NR::Matrix (NR::translate (-center)) *
294             NR::Matrix (NR::scale(1, height/width)) *
295             NR::Matrix (NR::translate (center));
297         gr->gradientTransform = squeeze;
298         {
299             gchar *c=sp_svg_transform_write(gr->gradientTransform);
300             SP_OBJECT_REPR(gr)->setAttribute("gradientTransform", c);
301             g_free(c);
302         }
303     } else {
304         sp_repr_set_svg_double(repr, "x1", (center - NR::Point(width/2, 0))[NR::X]);
305         sp_repr_set_svg_double(repr, "y1", (center - NR::Point(width/2, 0))[NR::Y]);
306         sp_repr_set_svg_double(repr, "x2", (center + NR::Point(width/2, 0))[NR::X]);
307         sp_repr_set_svg_double(repr, "y2", (center + NR::Point(width/2, 0))[NR::Y]);
308     }
310     // set the gradientUnits
311     repr->setAttribute("gradientUnits", "userSpaceOnUse");
313     return gr;
316 /**
317  * Convert an item's gradient to userspace if necessary, also fork it if necessary.
318  * @return The new gradient.
319  */
320 SPGradient *
321 sp_gradient_convert_to_userspace(SPGradient *gr, SPItem *item, gchar const *property)
323     g_return_val_if_fail(SP_IS_GRADIENT(gr), NULL);
325     // First, fork it if it is shared
326     gr = sp_gradient_fork_private_if_necessary(gr, sp_gradient_get_vector(gr, FALSE),
327                                                SP_IS_RADIALGRADIENT(gr) ? SP_GRADIENT_TYPE_RADIAL : SP_GRADIENT_TYPE_LINEAR, SP_OBJECT(item));
329     if (gr->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) {
331         Inkscape::XML::Node *repr = SP_OBJECT_REPR(gr);
333         // calculate the bbox of the item
334         sp_document_ensure_up_to_date(SP_OBJECT_DOCUMENT(item));
335         NR::Matrix bbox2user;
336         NR::Maybe<NR::Rect> bbox = item->getBounds(NR::identity()); // we need "true" bbox without item_i2d_affine
337         if ( bbox && !bbox->isEmpty() ) {
338             bbox2user = NR::Matrix(bbox->dimensions()[NR::X], 0,
339                                    0, bbox->dimensions()[NR::Y],
340                                    bbox->min()[NR::X], bbox->min()[NR::Y]);
341         } else {
342             // would be degenerate otherwise
343             bbox2user = NR::identity();
344         }
346         /* skew is the additional transform, defined by the proportions of the item, that we need
347          * to apply to the gradient in order to work around this weird bit from SVG 1.1
348          * (http://www.w3.org/TR/SVG11/pservers.html#LinearGradients):
349          *
350          *   When gradientUnits="objectBoundingBox" and gradientTransform is the identity
351          *   matrix, the stripes of the linear gradient are perpendicular to the gradient
352          *   vector in object bounding box space (i.e., the abstract coordinate system where
353          *   (0,0) is at the top/left of the object bounding box and (1,1) is at the
354          *   bottom/right of the object bounding box). When the object's bounding box is not
355          *   square, the stripes that are conceptually perpendicular to the gradient vector
356          *   within object bounding box space will render non-perpendicular relative to the
357          *   gradient vector in user space due to application of the non-uniform scaling
358          *   transformation from bounding box space to user space.
359          */
360         NR::Matrix skew = bbox2user;
361         double exp = skew.expansion();
362         skew[0] /= exp;
363         skew[1] /= exp;
364         skew[2] /= exp;
365         skew[3] /= exp;
366         skew[4] = 0;
367         skew[5] = 0;
369         // apply skew to the gradient
370         gr->gradientTransform = skew;
371         {
372             gchar *c=sp_svg_transform_write(gr->gradientTransform);
373             SP_OBJECT_REPR(gr)->setAttribute("gradientTransform", c);
374             g_free(c);
375         }
377         // Matrix to convert points to userspace coords; postmultiply by inverse of skew so
378         // as to cancel it out when it's applied to the gradient during rendering
379         NR::Matrix point_convert = bbox2user * skew.inverse();
381         if (SP_IS_RADIALGRADIENT(gr)) {
382             SPRadialGradient *rg = SP_RADIALGRADIENT(gr);
384             // original points in the bbox coords
385             NR::Point c_b = NR::Point(rg->cx.computed, rg->cy.computed);
386             NR::Point f_b = NR::Point(rg->fx.computed, rg->fy.computed);
387             double r_b = rg->r.computed;
389             // converted points in userspace coords
390             NR::Point c_u = c_b * point_convert;
391             NR::Point f_u = f_b * point_convert;
392             double r_u = r_b * point_convert.expansion();
394             sp_repr_set_svg_double(repr, "cx", c_u[NR::X]);
395             sp_repr_set_svg_double(repr, "cy", c_u[NR::Y]);
396             sp_repr_set_svg_double(repr, "fx", f_u[NR::X]);
397             sp_repr_set_svg_double(repr, "fy", f_u[NR::Y]);
398             sp_repr_set_svg_double(repr, "r", r_u);
400         } else {
401             SPLinearGradient *lg = SP_LINEARGRADIENT(gr);
403             NR::Point p1_b = NR::Point(lg->x1.computed, lg->y1.computed);
404             NR::Point p2_b = NR::Point(lg->x2.computed, lg->y2.computed);
406             NR::Point p1_u = p1_b * point_convert;
407             NR::Point p2_u = p2_b * point_convert;
409             sp_repr_set_svg_double(repr, "x1", p1_u[NR::X]);
410             sp_repr_set_svg_double(repr, "y1", p1_u[NR::Y]);
411             sp_repr_set_svg_double(repr, "x2", p2_u[NR::X]);
412             sp_repr_set_svg_double(repr, "y2", p2_u[NR::Y]);
413         }
415         // set the gradientUnits
416         repr->setAttribute("gradientUnits", "userSpaceOnUse");
417     }
419     // apply the gradient to the item (may be necessary if we forked it); not recursive
420     // generally because grouped items will be taken care of later (we're being called
421     // from sp_item_adjust_paint_recursive); however text and all its children should all
422     // refer to one gradient, hence the recursive call for text (because we can't/don't
423     // want to access tspans and set gradients on them separately)
424     if (SP_IS_TEXT(item))
425         sp_style_set_property_url(SP_OBJECT(item), property, SP_OBJECT(gr), true);
426     else
427         sp_style_set_property_url(SP_OBJECT(item), property, SP_OBJECT(gr), false);
429     return gr;
432 void
433 sp_gradient_transform_multiply(SPGradient *gradient, NR::Matrix postmul, bool set)
435     if (set) {
436         gradient->gradientTransform = postmul;
437     } else {
438         gradient->gradientTransform *= postmul; // fixme: get gradient transform by climbing to hrefs?
439     }
440     gradient->gradientTransform_set = TRUE;
442     gchar *c=sp_svg_transform_write(gradient->gradientTransform);
443     SP_OBJECT_REPR(gradient)->setAttribute("gradientTransform", c);
444     g_free(c);
447 SPGradient *
448 sp_item_gradient (SPItem *item, bool fill_or_stroke)
450     SPStyle *style = SP_OBJECT_STYLE (item);
451     SPGradient *gradient = NULL;
453     if (fill_or_stroke) {
454         if (style && (style->fill.type == SP_PAINT_TYPE_PAINTSERVER)) {
455             SPObject *server = SP_OBJECT_STYLE_FILL_SERVER(item);
456             if (SP_IS_GRADIENT (server)) {
457                 gradient = SP_GRADIENT (server);
458             }
459         }
460     } else {
461         if (style && (style->stroke.type == SP_PAINT_TYPE_PAINTSERVER)) {
462             SPObject *server = SP_OBJECT_STYLE_STROKE_SERVER(item);
463             if (SP_IS_GRADIENT (server)) {
464                 gradient = SP_GRADIENT (server);
465             }
466         }
467     }
469    return gradient;
473 SPStop*
474 sp_first_stop(SPGradient *gradient)
476   for (SPObject *ochild = sp_object_first_child(gradient); ochild != NULL; ochild = SP_OBJECT_NEXT(ochild)) {
477         if (SP_IS_STOP (ochild))
478                 return SP_STOP(ochild);
479   }
480   return NULL;
483 SPStop*
484 sp_prev_stop(SPStop *stop, SPGradient *gradient)
486         if (sp_object_first_child(SP_OBJECT(gradient)) == SP_OBJECT(stop))
487                 return NULL;
488         SPObject *found = NULL;
489         for ( SPObject *ochild = sp_object_first_child(SP_OBJECT(gradient)) ; ochild != NULL ; ochild = SP_OBJECT_NEXT(ochild) ) {
490                 if (SP_IS_STOP (ochild)) {
491                         found = ochild;
492                 }
493                 if (SP_OBJECT_NEXT(ochild) == SP_OBJECT(stop) || SP_OBJECT(ochild) == SP_OBJECT(stop)) {
494                         break;
495                 }
496         }
497         return SP_STOP(found);
500 SPStop*
501 sp_next_stop(SPStop *stop)
503   for (SPObject *ochild = SP_OBJECT_NEXT(stop); ochild != NULL; ochild = SP_OBJECT_NEXT(ochild)) {
504         if (SP_IS_STOP (ochild))
505                 return SP_STOP(ochild);
506   }
507   return NULL;
510 SPStop*
511 sp_last_stop(SPGradient *gradient)
513     for (SPStop *stop = sp_first_stop (gradient); stop != NULL; stop = sp_next_stop (stop)) {
514         if (sp_next_stop (stop) == NULL)
515                 return stop;
516   }
517   return NULL;
518
520 SPStop*
521 sp_get_stop_i(SPGradient *gradient, guint stop_i)
522 {            
523   SPStop *stop = sp_first_stop (gradient);
524   
525   for (guint i=0; i < stop_i; i++) {
526     if (!stop) return NULL;  
527     stop = sp_next_stop (stop);    
528   }  
529     
530   return stop;
534 void
535 sp_item_gradient_edit_stop (SPItem *item, guint point_type, guint point_i, bool fill_or_stroke)
537     SPGradient *gradient = sp_item_gradient (item, fill_or_stroke);
539     if (!gradient || !SP_IS_GRADIENT(gradient))
540         return;
542     SPGradient *vector = sp_gradient_get_vector (gradient, false);
543     switch (point_type) {
544         case POINT_LG_BEGIN:
545         case POINT_RG_CENTER:
546         case POINT_RG_FOCUS:
547         {
548             GtkWidget *dialog = sp_gradient_vector_editor_new (vector, sp_first_stop (vector));
549             gtk_widget_show (dialog);
550         }
551         break;
553         case POINT_LG_END:
554         case POINT_RG_R1:
555         case POINT_RG_R2:
556         {
557             GtkWidget *dialog = sp_gradient_vector_editor_new (vector, sp_last_stop (vector));
558             gtk_widget_show (dialog);
559         }
560         break;
561         
562         case POINT_LG_MID:
563         case POINT_RG_MID1:
564         case POINT_RG_MID2:
565         {
566             GtkWidget *dialog = sp_gradient_vector_editor_new (vector, sp_get_stop_i (vector, point_i));
567             gtk_widget_show (dialog);
568         }
569         break;
570         default:
571             break;
572     }
575 guint32
576 sp_item_gradient_stop_query_style (SPItem *item, guint point_type, guint point_i, bool fill_or_stroke)
578     SPGradient *gradient = sp_item_gradient (item, fill_or_stroke);
580     if (!gradient || !SP_IS_GRADIENT(gradient))
581         return 0;
583     SPGradient *vector = sp_gradient_get_vector (gradient, false);
585     if (!vector) // orphan!
586         return 0; // what else to do?
588     switch (point_type) {
589         case POINT_LG_BEGIN:
590         case POINT_RG_CENTER:
591         case POINT_RG_FOCUS:
592         {
593             SPStop *first = sp_first_stop (vector);
594             if (first) {
595                 return sp_stop_get_rgba32(first);
596             }
597         }
598         break;
600         case POINT_LG_END:
601         case POINT_RG_R1:
602         case POINT_RG_R2:
603         {
604             SPStop *last = sp_last_stop (vector);
605             if (last) {
606                 return sp_stop_get_rgba32(last);
607             }
608         }
609         break;
610         
611         case POINT_LG_MID:
612         case POINT_RG_MID1:
613         case POINT_RG_MID2:
614         {
615             SPStop *stopi = sp_get_stop_i (vector, point_i);
616             if (stopi) {
617                 return sp_stop_get_rgba32(stopi);
618             }
619         }
620         break;
622         default:
623             break;
624     }
625     return 0;
628 void
629 sp_item_gradient_stop_set_style (SPItem *item, guint point_type, guint point_i, bool fill_or_stroke, SPCSSAttr *stop)
631     SPGradient *gradient = sp_item_gradient (item, fill_or_stroke);
633     if (!gradient || !SP_IS_GRADIENT(gradient))
634         return;
636     SPGradient *vector = sp_gradient_get_vector (gradient, false);
638     if (!vector) // orphan!
639         return;
641     vector = sp_gradient_fork_vector_if_necessary (vector);
642     if ( gradient != vector && gradient->ref->getObject() != vector ) {
643         sp_gradient_repr_set_link(SP_OBJECT_REPR(gradient), vector);
644     }
646     switch (point_type) {
647         case POINT_LG_BEGIN:
648         case POINT_RG_CENTER:
649         case POINT_RG_FOCUS:
650         {
651             SPStop *first = sp_first_stop (vector);
652             if (first) {
653                 sp_repr_css_change (SP_OBJECT_REPR (first), stop, "style");
654             }
655         }
656         break;
658         case POINT_LG_END:
659         case POINT_RG_R1:
660         case POINT_RG_R2:
661         {
662             SPStop *last = sp_last_stop (vector);
663             if (last) {
664                 sp_repr_css_change (SP_OBJECT_REPR (last), stop, "style");
665             }
666         }
667         break;
668         
669         case POINT_LG_MID:
670         case POINT_RG_MID1:
671         case POINT_RG_MID2:
672         {
673             SPStop *stopi = sp_get_stop_i (vector, point_i);
674             if (stopi) {
675                 sp_repr_css_change (SP_OBJECT_REPR (stopi), stop, "style");
676             }
677         }
678         break;
679            
680         default:
681             break;
682     }
685 void
686 sp_item_gradient_reverse_vector (SPItem *item, bool fill_or_stroke)
688     SPGradient *gradient = sp_item_gradient (item, fill_or_stroke);
689     if (!gradient || !SP_IS_GRADIENT(gradient))
690         return;
692     SPGradient *vector = sp_gradient_get_vector (gradient, false);
693     if (!vector) // orphan!
694         return;
696     vector = sp_gradient_fork_vector_if_necessary (vector);
697     if ( gradient != vector && gradient->ref->getObject() != vector ) {
698         sp_gradient_repr_set_link(SP_OBJECT_REPR(gradient), vector);
699     }
701     GSList *child_reprs = NULL;
702     GSList *child_objects = NULL;
703     std::vector<double> offsets;
704     for (SPObject *child = sp_object_first_child(vector);
705          child != NULL; child = SP_OBJECT_NEXT(child)) {
706         child_reprs = g_slist_prepend (child_reprs, SP_OBJECT_REPR(child));
707         child_objects = g_slist_prepend (child_objects, child);
708         offsets.push_back(sp_repr_get_double_attribute(SP_OBJECT_REPR(child), "offset", 0));
709     }
711     GSList *child_copies = NULL;
712     for (GSList *i = child_reprs; i != NULL; i = i->next) {
713         Inkscape::XML::Node *repr = (Inkscape::XML::Node *) i->data;
714         Inkscape::XML::Document *xml_doc = SP_OBJECT_REPR(vector)->document();
715         child_copies = g_slist_append (child_copies, repr->duplicate(xml_doc));
716     }
719     for (GSList *i = child_objects; i != NULL; i = i->next) {
720         SPObject *child = SP_OBJECT (i->data);
721         child->deleteObject();
722     }
724     std::vector<double>::iterator iter = offsets.end() - 1;
725     for (GSList *i = child_copies; i != NULL; i = i->next) {
726         Inkscape::XML::Node *copy = (Inkscape::XML::Node *) i->data;
727         vector->appendChildRepr(copy);
728         sp_repr_set_svg_double (copy, "offset", 1 - *iter);
729         iter --;
730         Inkscape::GC::release(copy);
731     }
733     g_slist_free (child_reprs);
734     g_slist_free (child_copies);
735     g_slist_free (child_objects);
739 // FIXME: make general global function
740 static double
741 get_offset_between_points (NR::Point p, NR::Point begin, NR::Point end)
743     double length = NR::L2(end - begin);
744     NR::Point be = (end - begin) / length;
745     double r = NR::dot(p - begin, be);
746         
747     if (r < 0.0) return 0.0;
748     if (r > length) return 1.0;    
749     
750     return (r / length);
754 /**
755 Set the position of point point_type of the gradient applied to item (either fill_or_stroke) to
756 p_w (in desktop coordinates). Write_repr if you want the change to become permanent.
757 */
758 void
759 sp_item_gradient_set_coords (SPItem *item, guint point_type, guint point_i, NR::Point p_w, bool fill_or_stroke, bool write_repr, bool scale)
761     SPGradient *gradient = sp_item_gradient (item, fill_or_stroke);
763     if (!gradient || !SP_IS_GRADIENT(gradient))
764         return;
766     gradient = sp_gradient_convert_to_userspace (gradient, item, fill_or_stroke? "fill" : "stroke");
768     NR::Matrix i2d = sp_item_i2d_affine (item);
769     NR::Point p = p_w * i2d.inverse();
770     p *= (gradient->gradientTransform).inverse();
771     // now p is in gradient's original coordinates
773     Inkscape::XML::Node *repr = SP_OBJECT_REPR(gradient);
775     if (SP_IS_LINEARGRADIENT(gradient)) {
776         SPLinearGradient *lg = SP_LINEARGRADIENT(gradient);
777         switch (point_type) {
778             case POINT_LG_BEGIN:
779                 if (scale) {
780                     lg->x2.computed += (lg->x1.computed - p[NR::X]);
781                     lg->y2.computed += (lg->y1.computed - p[NR::Y]);
782                 }
783                 lg->x1.computed = p[NR::X];
784                 lg->y1.computed = p[NR::Y];
785                 if (write_repr) {
786                     if (scale) {
787                         sp_repr_set_svg_double(repr, "x2", lg->x2.computed);
788                         sp_repr_set_svg_double(repr, "y2", lg->y2.computed);
789                     }
790                     sp_repr_set_svg_double(repr, "x1", lg->x1.computed);
791                     sp_repr_set_svg_double(repr, "y1", lg->y1.computed);
792                 } else {
793                     SP_OBJECT (gradient)->requestModified(SP_OBJECT_MODIFIED_FLAG);
794                 }
795                 break;
796             case POINT_LG_END:
797                 if (scale) {
798                     lg->x1.computed += (lg->x2.computed - p[NR::X]);
799                     lg->y1.computed += (lg->y2.computed - p[NR::Y]);
800                 }
801                 lg->x2.computed = p[NR::X];
802                 lg->y2.computed = p[NR::Y];
803                 if (write_repr) {
804                     if (scale) {
805                         sp_repr_set_svg_double(repr, "x1", lg->x1.computed);
806                         sp_repr_set_svg_double(repr, "y1", lg->y1.computed);
807                     }
808                     sp_repr_set_svg_double(repr, "x2", lg->x2.computed);
809                     sp_repr_set_svg_double(repr, "y2", lg->y2.computed);
810                 } else {
811                     SP_OBJECT (gradient)->requestModified(SP_OBJECT_MODIFIED_FLAG);
812                 }
813                         break;
814             case POINT_LG_MID:
815             {                              
816                 // using X-coordinates only to determine the offset, assuming p has been snapped to the vector from begin to end.
817                 double offset = get_offset_between_points (p, NR::Point(lg->x1.computed, lg->y1.computed), NR::Point(lg->x2.computed, lg->y2.computed));
818                 SPGradient *vector = sp_gradient_get_forked_vector_if_necessary (lg, false);
819                 sp_gradient_ensure_vector(lg);
820                 lg->vector.stops.at(point_i).offset = offset;
821                 SPStop* stopi = sp_get_stop_i(vector, point_i);
822                 stopi->offset = offset;
823                 if (write_repr) {
824                     sp_repr_set_css_double(SP_OBJECT_REPR(stopi), "offset", stopi->offset);
825                 } else {
826                     SP_OBJECT (stopi)->requestModified(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG);
827                 }
828             }
829             break;
830                 default:
831                         break;
832                 }
833         } else if (SP_IS_RADIALGRADIENT(gradient)) {
834         SPRadialGradient *rg = SP_RADIALGRADIENT(gradient);
835         NR::Point c (rg->cx.computed, rg->cy.computed);
836         NR::Point c_w = c * gradient->gradientTransform * i2d; // now in desktop coords
837         if ((point_type == POINT_RG_R1 || point_type == POINT_RG_R2) && NR::L2 (p_w - c_w) < 1e-3) {
838             // prevent setting a radius too close to the center
839             return;
840         }
841         NR::Matrix new_transform;
842         bool transform_set = false;
844                 switch (point_type) {
845                 case POINT_RG_CENTER:
846                         rg->fx.computed = p[NR::X] + (rg->fx.computed - rg->cx.computed);
847                         rg->fy.computed = p[NR::Y] + (rg->fy.computed - rg->cy.computed);
848                         rg->cx.computed = p[NR::X];
849                         rg->cy.computed = p[NR::Y];
850                         if (write_repr) {
851                                 sp_repr_set_svg_double(repr, "fx", rg->fx.computed);
852                                 sp_repr_set_svg_double(repr, "fy", rg->fy.computed);
853                                 sp_repr_set_svg_double(repr, "cx", rg->cx.computed);
854                                 sp_repr_set_svg_double(repr, "cy", rg->cy.computed);
855                         } else {
856                                 SP_OBJECT (gradient)->requestModified(SP_OBJECT_MODIFIED_FLAG);
857                         }
858                         break;
859                 case POINT_RG_FOCUS:
860                         rg->fx.computed = p[NR::X];
861                         rg->fy.computed = p[NR::Y];
862                         if (write_repr) {
863                                 sp_repr_set_svg_double(repr, "fx", rg->fx.computed);
864                                 sp_repr_set_svg_double(repr, "fy", rg->fy.computed);
865                         } else {
866                                 SP_OBJECT (gradient)->requestModified(SP_OBJECT_MODIFIED_FLAG);
867                         }
868                         break;
869                 case POINT_RG_R1:
870                         {
871                 NR::Point r1_w = (c + NR::Point(rg->r.computed, 0)) * gradient->gradientTransform * i2d;
872                                 double r1_angle = NR::atan2(r1_w - c_w);
873                                 double move_angle = NR::atan2(p_w - c_w) - r1_angle;
874                                 double move_stretch = NR::L2(p_w - c_w) / NR::L2(r1_w - c_w);
876                                 NR::Matrix move = NR::Matrix (NR::translate (-c_w)) *
877                                                                                                  NR::Matrix (NR::rotate(-r1_angle)) *
878                                                                                                  NR::Matrix (NR::scale(move_stretch, scale? move_stretch : 1)) *
879                                                                                                  NR::Matrix (NR::rotate(r1_angle)) *
880                                                                                                  NR::Matrix (NR::rotate(move_angle)) *
881                                                                                                  NR::Matrix (NR::translate (c_w));
883                                 new_transform = gradient->gradientTransform * i2d * move * i2d.inverse();
884                                 transform_set = true;
886                                 break;
887                         }
888                 case POINT_RG_R2:
889                         {
890                                 NR::Point r2_w = (c + NR::Point(0, -rg->r.computed)) * gradient->gradientTransform * i2d;
891                                 double r2_angle = NR::atan2(r2_w - c_w);
892                                 double move_angle = NR::atan2(p_w - c_w) - r2_angle;
893                                 double move_stretch = NR::L2(p_w - c_w) / NR::L2(r2_w - c_w);
895                                 NR::Matrix move = NR::Matrix (NR::translate (-c_w)) *
896                                                                                                  NR::Matrix (NR::rotate(-r2_angle)) *
897                                                                                                  NR::Matrix (NR::scale(move_stretch, scale? move_stretch : 1)) *
898                                                                                                  NR::Matrix (NR::rotate(r2_angle)) *
899                                                                                                  NR::Matrix (NR::rotate(move_angle)) *
900                                                                                                  NR::Matrix (NR::translate (c_w));
902                                 new_transform = gradient->gradientTransform * i2d * move * i2d.inverse();
903                                 transform_set = true;
905                 break;
906             }
907         case POINT_RG_MID1:
908             {
909                 NR::Point start = NR::Point (rg->cx.computed, rg->cy.computed);
910                  NR::Point end   = NR::Point (rg->cx.computed + rg->r.computed, rg->cy.computed);
911                 double offset = get_offset_between_points (p, start, end);
912                 SPGradient *vector = sp_gradient_get_forked_vector_if_necessary (rg, false);
913                 sp_gradient_ensure_vector(rg);
914                 rg->vector.stops.at(point_i).offset = offset; 
915                 SPStop* stopi = sp_get_stop_i(vector, point_i);
916                 stopi->offset = offset;
917                 if (write_repr) {
918                     sp_repr_set_css_double(SP_OBJECT_REPR(stopi), "offset", stopi->offset);
919                 } else {
920                     SP_OBJECT (stopi)->requestModified(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG);
921                 }
922                 break;
923             }
924         case POINT_RG_MID2:
925                 NR::Point start = NR::Point (rg->cx.computed, rg->cy.computed);
926                 NR::Point end   = NR::Point (rg->cx.computed, rg->cy.computed - rg->r.computed);
927                 double offset = get_offset_between_points (p, start, end);
928                 SPGradient *vector = sp_gradient_get_forked_vector_if_necessary(rg, false);
929                 sp_gradient_ensure_vector(rg);
930                 rg->vector.stops.at(point_i).offset = offset;
931                 SPStop* stopi = sp_get_stop_i(vector, point_i);
932                 stopi->offset = offset;
933                 if (write_repr) {
934                     sp_repr_set_css_double(SP_OBJECT_REPR(stopi), "offset", stopi->offset);
935                 } else {
936                     SP_OBJECT (stopi)->requestModified(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG);
937                 }
938                 break;
939             }
940                 if (transform_set) {
941                                 gradient->gradientTransform = new_transform;
942                                 gradient->gradientTransform_set = TRUE;
943                                 if (write_repr) {
944                                         gchar *s=sp_svg_transform_write(gradient->gradientTransform);
945                                         SP_OBJECT_REPR(gradient)->setAttribute("gradientTransform", s);
946                                         g_free(s);
947                                 } else {
948                                         SP_OBJECT (gradient)->requestModified(SP_OBJECT_MODIFIED_FLAG);
949                                 }
950                 }
951         }
954 SPGradient *
955 sp_item_gradient_get_vector (SPItem *item, bool fill_or_stroke)
957     SPGradient *gradient = sp_item_gradient (item, fill_or_stroke);
959     if (gradient)
960         return sp_gradient_get_vector (gradient, false);
961     return NULL;
964 SPGradientSpread
965 sp_item_gradient_get_spread (SPItem *item, bool fill_or_stroke)
967     SPGradient *gradient = sp_item_gradient (item, fill_or_stroke);
969     if (gradient)
970         return sp_gradient_get_spread (gradient);
971     return SP_GRADIENT_SPREAD_PAD;
975 /**
976 Returns the position of point point_type of the gradient applied to item (either fill_or_stroke),
977 in desktop coordinates.
978 */
980 NR::Point
981 sp_item_gradient_get_coords (SPItem *item, guint point_type, guint point_i, bool fill_or_stroke)
983     SPGradient *gradient = sp_item_gradient (item, fill_or_stroke);
985     NR::Point p (0, 0);
987     if (!gradient)
988         return p;
990     if (SP_IS_LINEARGRADIENT(gradient)) {
991         SPLinearGradient *lg = SP_LINEARGRADIENT(gradient);
992         switch (point_type) {
993             case POINT_LG_BEGIN:
994                 p = NR::Point (lg->x1.computed, lg->y1.computed);
995                 break;
996             case POINT_LG_END:
997                 p = NR::Point (lg->x2.computed, lg->y2.computed);
998                 break;
999             case POINT_LG_MID:
1000                 {   
1001                     gdouble offset = lg->vector.stops.at(point_i).offset;
1002                     p = (1-offset) * NR::Point(lg->x1.computed, lg->y1.computed) + offset * NR::Point(lg->x2.computed, lg->y2.computed);
1003                 }
1004                 break;
1005         }
1006     } else     if (SP_IS_RADIALGRADIENT(gradient)) {
1007         SPRadialGradient *rg = SP_RADIALGRADIENT(gradient);
1008         switch (point_type) {
1009             case POINT_RG_CENTER:
1010                 p = NR::Point (rg->cx.computed, rg->cy.computed);
1011                 break;
1012             case POINT_RG_FOCUS:
1013                 p = NR::Point (rg->fx.computed, rg->fy.computed);
1014                 break;
1015             case POINT_RG_R1:
1016                 p = NR::Point (rg->cx.computed + rg->r.computed, rg->cy.computed);
1017                 break;
1018             case POINT_RG_R2:
1019                 p = NR::Point (rg->cx.computed, rg->cy.computed - rg->r.computed);
1020                 break;
1021             case POINT_RG_MID1:
1022                 {
1023                     gdouble offset = rg->vector.stops.at(point_i).offset;
1024                     p = (1-offset) * NR::Point (rg->cx.computed, rg->cy.computed) + offset * NR::Point(rg->cx.computed + rg->r.computed, rg->cy.computed);
1025                 }        
1026                 break;
1027             case POINT_RG_MID2:
1028                 {
1029                     gdouble offset = rg->vector.stops.at(point_i).offset;
1030                     p = (1-offset) * NR::Point (rg->cx.computed, rg->cy.computed) + offset * NR::Point(rg->cx.computed, rg->cy.computed - rg->r.computed);
1031                 }        
1032                 break;
1033         }
1034     }
1036     if (SP_GRADIENT(gradient)->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) {
1037         sp_document_ensure_up_to_date(SP_OBJECT_DOCUMENT(item));
1038         NR::Maybe<NR::Rect> bbox = item->getBounds(NR::identity()); // we need "true" bbox without item_i2d_affine
1039         if (bbox) {
1040             p *= NR::Matrix(bbox->dimensions()[NR::X], 0,
1041                             0, bbox->dimensions()[NR::Y],
1042                             bbox->min()[NR::X], bbox->min()[NR::Y]);
1043         }
1044     }
1045     p *= NR::Matrix(gradient->gradientTransform) * sp_item_i2d_affine(item);
1046     return p;
1050 /**
1051  * Sets item fill or stroke to the gradient of the specified type with given vector, creating
1052  * new private gradient, if needed.
1053  * gr has to be a normalized vector.
1054  */
1056 SPGradient *
1057 sp_item_set_gradient(SPItem *item, SPGradient *gr, SPGradientType type, bool is_fill)
1059     g_return_val_if_fail(item != NULL, NULL);
1060     g_return_val_if_fail(SP_IS_ITEM(item), NULL);
1061     g_return_val_if_fail(gr != NULL, NULL);
1062     g_return_val_if_fail(SP_IS_GRADIENT(gr), NULL);
1063     g_return_val_if_fail(gr->state == SP_GRADIENT_STATE_VECTOR, NULL);
1065     SPStyle *style = SP_OBJECT_STYLE(item);
1066     g_assert(style != NULL);
1068     guint style_type = is_fill? style->fill.type : style->stroke.type;
1069     SPPaintServer *ps = NULL;
1070     if (style_type == SP_PAINT_TYPE_PAINTSERVER)
1071         ps = is_fill? SP_STYLE_FILL_SERVER(style) : SP_STYLE_STROKE_SERVER(style);
1073     if (ps
1074         && ( (type == SP_GRADIENT_TYPE_LINEAR && SP_IS_LINEARGRADIENT(ps)) ||
1075              (type == SP_GRADIENT_TYPE_RADIAL && SP_IS_RADIALGRADIENT(ps))   ) )
1076     {
1078         /* Current fill style is the gradient of the required type */
1079         SPGradient *current = SP_GRADIENT(ps);
1081         //g_print("hrefcount %d   count %d\n", SP_OBJECT_HREFCOUNT(ig), count_gradient_hrefs(SP_OBJECT(item), ig));
1083         if (SP_OBJECT_HREFCOUNT(current) == 1 ||
1084             SP_OBJECT_HREFCOUNT(current) == count_gradient_hrefs(SP_OBJECT(item), current)) {
1086             // current is private and it's either used once, or all its uses are by children of item;
1087             // so just change its href to vector
1089             if ( current != gr && sp_gradient_get_vector(current, false) != gr ) {
1090                 /* href is not the vector */
1091                 sp_gradient_repr_set_link(SP_OBJECT_REPR(current), gr);
1092             }
1093             SP_OBJECT(item)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG);
1094             return current;
1096         } else {
1098             // the gradient is not private, or it is shared with someone else;
1099             // normalize it (this includes creating new private if necessary)
1100             SPGradient *normalized = sp_gradient_fork_private_if_necessary(current, gr, type, item);
1102             g_return_val_if_fail(normalized != NULL, NULL);
1104             if (normalized != current) {
1106                 /* We have to change object style here; recursive because this is used from
1107                  * fill&stroke and must work for groups etc. */
1108                 sp_style_set_property_url(SP_OBJECT(item), is_fill? "fill" : "stroke", SP_OBJECT(normalized), true);
1109             }
1110             SP_OBJECT(item)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG);
1111             return normalized;
1112         }
1114     } else {
1115         /* Current fill style is not a gradient or wrong type, so construct everything */
1116         SPGradient *constructed = sp_gradient_get_private_normalized(SP_OBJECT_DOCUMENT(item), gr, type);
1117         constructed = sp_gradient_reset_to_userspace(constructed, item);
1118         sp_style_set_property_url(SP_OBJECT(item), ( is_fill ? "fill" : "stroke" ), SP_OBJECT(constructed), true);
1119         SP_OBJECT(item)->requestDisplayUpdate(( SP_OBJECT_MODIFIED_FLAG |
1120                                                 SP_OBJECT_STYLE_MODIFIED_FLAG ));
1121         return constructed;
1122     }
1125 static void
1126 sp_gradient_repr_set_link(Inkscape::XML::Node *repr, SPGradient *link)
1128     g_return_if_fail(repr != NULL);
1129     g_return_if_fail(link != NULL);
1130     g_return_if_fail(SP_IS_GRADIENT(link));
1132     gchar *ref;
1133     if (link) {
1134         gchar const *id = SP_OBJECT_ID(link);
1135         size_t const len = strlen(id);
1136         ref = (gchar*) alloca(len + 2);
1137         *ref = '#';
1138         memcpy(ref + 1, id, len + 1);
1139     } else {
1140         ref = NULL;
1141     }
1143     repr->setAttribute("xlink:href", ref);
1146 /*
1147  * Get default normalized gradient vector of document, create if there is none
1148  */
1150 SPGradient *
1151 sp_document_default_gradient_vector(SPDocument *document, guint32 color)
1153     SPDefs *defs = (SPDefs *) SP_DOCUMENT_DEFS(document);
1154     Inkscape::XML::Document *xml_doc = sp_document_repr_doc(document);
1156     Inkscape::XML::Node *repr = xml_doc->createElement("svg:linearGradient");
1158     repr->setAttribute("inkscape:collect", "always");
1159     // set here, but removed when it's edited in the gradient editor
1160     // to further reduce clutter, we could
1161     // (1) here, search gradients by color and return what is found without duplication
1162     // (2) in fill & stroke, show only one copy of each gradient in list
1164     Inkscape::XML::Node *stop = xml_doc->createElement("svg:stop");
1166     gchar b[64];
1167     sp_svg_write_color(b, 64, color);
1169     {
1170         gchar *t = g_strdup_printf("stop-color:%s;stop-opacity:1;", b);
1171         stop->setAttribute("style", t);
1172         g_free(t);
1173     }
1175     stop->setAttribute("offset", "0");
1177     repr->appendChild(stop);
1178     Inkscape::GC::release(stop);
1180     stop = xml_doc->createElement("svg:stop");
1182     {
1183         gchar *t = g_strdup_printf("stop-color:%s;stop-opacity:0;", b);
1184         stop->setAttribute("style", t);
1185         g_free(t);
1186     }
1188     stop->setAttribute("offset", "1");
1190     repr->appendChild(stop);
1191     Inkscape::GC::release(stop);
1193     SP_OBJECT_REPR(defs)->addChild(repr, NULL);
1194     Inkscape::GC::release(repr);
1196     /* fixme: This does not look like nice */
1197     SPGradient *gr;
1198     gr = (SPGradient *) document->getObjectByRepr(repr);
1199     g_assert(gr != NULL);
1200     g_assert(SP_IS_GRADIENT(gr));
1201     /* fixme: Maybe add extra sanity check here */
1202     gr->state = SP_GRADIENT_STATE_VECTOR;
1204     return gr;
1207 /**
1208 Return the preferred vector for \a o, made from (in order of preference) its current vector,
1209 current fill or stroke color, or from desktop style if \a o is NULL or doesn't have style.
1210 */
1211 SPGradient *
1212 sp_gradient_vector_for_object(SPDocument *const doc, SPDesktop *const desktop,
1213                               SPObject *const o, bool const is_fill)
1215     guint32 rgba = 0;
1216     if (o == NULL || SP_OBJECT_STYLE(o) == NULL) {
1217         rgba = sp_desktop_get_color(desktop, is_fill);
1218     } else {
1219         // take the color of the object
1220         SPStyle const &style = *SP_OBJECT_STYLE(o);
1221         SPIPaint const &paint = ( is_fill
1222                                   ? style.fill
1223                                   : style.stroke );
1224         if (paint.type == SP_PAINT_TYPE_COLOR) {
1225             rgba = sp_color_get_rgba32_ualpha(&paint.value.color, 0xff);
1226         } else if (paint.type == SP_PAINT_TYPE_PAINTSERVER) {
1227             SPObject *server = is_fill? SP_OBJECT_STYLE_FILL_SERVER(o) : SP_OBJECT_STYLE_STROKE_SERVER(o);
1228             if (SP_IS_GRADIENT (server)) {
1229                 return sp_gradient_get_vector(SP_GRADIENT (server), TRUE);
1230             } else {
1231                 rgba = sp_desktop_get_color(desktop, is_fill);
1232             }
1233         } else {
1234             // if o doesn't use flat color, then take current color of the desktop.
1235             rgba = sp_desktop_get_color(desktop, is_fill);
1236         }
1237     }
1239     return sp_document_default_gradient_vector(doc, rgba);
1243 /*
1244   Local Variables:
1245   mode:c++
1246   c-file-style:"stroustrup"
1247   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1248   indent-tabs-mode:nil
1249   fill-column:99
1250   End:
1251 */
1252 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :