Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
gradient-chemistry.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Various utility methods for gradients
4 *
5 * Authors:
6 * Lauris Kaplinski <lauris@kaplinski.com>
7 * bulia byak
8 * Johan Engelen <j.b.c.engelen@ewi.utwente.nl>
9 * Jon A. Cruz <jon@joncruz.org>
10 * Abhishek Sharma
11 * Tavmjong Bah <tavmjong@free.fr>
12 *
13 * Copyright (C) 2012 Tavmjong Bah
14 * Copyright (C) 2010 Authors
15 * Copyright (C) 2007 Johan Engelen
16 * Copyright (C) 2001-2005 authors
17 * Copyright (C) 2001 Ximian, Inc.
18 *
19 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
20 */
21
22#include <glibmm/i18n.h>
23
24#include <2geom/transforms.h>
25#include <2geom/bezier-curve.h>
26#include <2geom/crossing.h>
27#include <2geom/line.h>
28
29#include "desktop-style.h"
30#include "desktop.h"
31#include "document.h"
32#include "document-undo.h"
33#include "gradient-chemistry.h"
34#include "gradient-drag.h"
35#include "selection.h"
36
37#include "object/sp-defs.h"
42#include "object/sp-stop.h"
43#include "object/sp-text.h"
44#include "object/sp-tspan.h"
45#include "object/sp-root.h"
46#include "style.h"
47
48#include "svg/svg.h"
50
51#include "ui/icon-names.h"
52#include "ui/tools/tool-base.h"
55
56#include "colors/color.h"
57
58#define noSP_GR_VERBOSE
59
61
62namespace {
63
65
66std::vector<Inkscape::PaintTarget> vectorOfPaintTargets(paintTargetItems, paintTargetItems + (sizeof(paintTargetItems) / sizeof(paintTargetItems[0])));
67
68} // namespace
69
70namespace Inkscape {
71
72std::vector<PaintTarget> const &allPaintTargets()
73{
74 return vectorOfPaintTargets;
75}
76
77} // namespace Inkscape
78
79// Terminology:
80//
81// "vector" is a gradient that has stops but not position coords. It can be referenced by one or
82// more privates. Objects should not refer to it directly. It has no radial/linear distinction.
83//
84// "array" is a gradient that has mesh rows and patches. It may or may not have "x" and "y" attributes.
85// An array does have spacial information so it cannot be normalized like a "vector".
86//
87// "shared" is either a "vector" or "array" that is shared between multiple objects.
88//
89// "private" is a gradient that is not shared. A private linear or radial gradient has no stops but
90// has position coords (e.g. center, radius etc for a radial); it references a "vector" for the
91// actual colors. A mesh may or may not reference an array. Each private is only used by one object.
92
94
96{
97#ifdef SP_GR_VERBOSE
98 g_message("sp_gradient_ensure_vector_normalized(%p)", gr);
99#endif
100 g_return_val_if_fail(gr != nullptr, NULL);
101 g_return_val_if_fail(!is<SPMeshGradient>(gr), NULL);
102
103 /* If we are already normalized vector, just return */
104 if (gr->state == SP_GRADIENT_STATE_VECTOR) return gr;
105 /* Fail, if we have wrong state set */
106 if (gr->state != SP_GRADIENT_STATE_UNKNOWN) {
107 g_warning("file %s: line %d: Cannot normalize private gradient to vector (%s)", __FILE__, __LINE__, gr->getId());
108 return nullptr;
109 }
110
111 /* First make sure we have vector directly defined (i.e. gr has its own stops) */
112 if ( !gr->hasStops() ) {
113 /* We do not have stops ourselves, so flatten stops as well */
114 gr->ensureVector();
115 g_assert(gr->vector.built);
116 // this adds stops from gr->vector as children to gr
117 gr->repr_write_vector ();
118 }
119
120 /* If gr hrefs some other gradient, remove the href */
121 if (gr->ref){
122 if (gr->ref->getObject()) {
123 // We are hrefing someone, so require flattening
124 gr->updateRepr(SP_OBJECT_WRITE_EXT | SP_OBJECT_WRITE_ALL);
125 sp_gradient_repr_set_link(gr->getRepr(), nullptr);
126 }
127 }
128
129 /* Everything is OK, set state flag */
131 return gr;
132}
133
139{
140#ifdef SP_GR_VERBOSE
141 g_message("sp_gradient_get_private_normalized(%p, %p, %d)", document, shared, type);
142#endif
143
144 g_return_val_if_fail(document != nullptr, NULL);
145 g_return_val_if_fail(shared != nullptr, NULL);
146 g_return_val_if_fail(shared->hasStops() || shared->hasPatches(), NULL);
147
148 SPDefs *defs = document->getDefs();
149
150 Inkscape::XML::Document *xml_doc = document->getReprDoc();
151 // create a new private gradient of the requested type
153 if (type == SP_GRADIENT_TYPE_LINEAR) {
154 repr = xml_doc->createElement("svg:linearGradient");
155 } else if(type == SP_GRADIENT_TYPE_RADIAL) {
156 repr = xml_doc->createElement("svg:radialGradient");
157 } else {
158 repr = xml_doc->createElement("svg:meshgradient");
159 }
160
161 // make auto collection optional
163 if (prefs->getBool("/option/gradient/auto_collect", true)) {
164 repr->setAttribute("inkscape:collect", "always");
165 } else {
166 repr->setAttribute("inkscape:collect", "never");
167 }
168
169 // link to shared
170 sp_gradient_repr_set_link(repr, shared);
171
172 /* Append the new private gradient to defs */
173 defs->getRepr()->appendChild(repr);
175
176 // get corresponding object
177 SPGradient *gr = static_cast<SPGradient *>(document->getObjectByRepr(repr));
178 g_assert(gr != nullptr);
179
180 return gr;
181}
182
187{
188 if (!o)
189 return 1;
190
191 guint i = 0;
192
193 SPStyle *style = o->style;
194 if (style
195 && style->fill.isPaintserver()
196 && is<SPGradient>(SP_STYLE_FILL_SERVER(style))
197 && cast<SPGradient>(SP_STYLE_FILL_SERVER(style)) == gr)
198 {
199 i ++;
200 }
201 if (style
202 && style->stroke.isPaintserver()
203 && is<SPGradient>(SP_STYLE_STROKE_SERVER(style))
204 && cast<SPGradient>(SP_STYLE_STROKE_SERVER(style)) == gr)
205 {
206 i ++;
207 }
208
209 for (auto& child: o->children) {
210 i += count_gradient_hrefs(&child, gr);
211 }
212
213 return i;
214}
215
216
221 SPGradientType type, SPObject *o)
222{
223#ifdef SP_GR_VERBOSE
224 g_message("sp_gradient_fork_private_if_necessary(%p, %p, %d, %p)", gr, shared, type, o);
225#endif
226 g_return_val_if_fail(gr != nullptr, NULL);
227
228 // Orphaned gradient, no shared with stops or patches at the end of the line; this used to be
229 // an assert
230 if ( !shared || !(shared->hasStops() || shared->hasPatches()) ) {
231 std::cerr << "sp_gradient_fork_private_if_necessary: Orphaned gradient" << std::endl;
232 return (gr);
233 }
234
235 // user is the object that uses this gradient; normally it's item but for tspans, we
236 // check its ancestor text so that tspans don't get different gradients from their
237 // texts.
238 SPObject *user = o;
239 while (is<SPTSpan>(user)) {
240 user = user->parent;
241 }
242
243 // Check the number of uses of the gradient within this object;
244 // if we are private and there are no other users,
245 if (!shared->isSwatch() && (gr->hrefcount <= count_gradient_hrefs(user, gr))) {
246 // check shared
247 if ( gr != shared && gr->ref->getObject() != shared ) {
248 /* our href is not the shared, and shared is different from gr; relink */
249 sp_gradient_repr_set_link(gr->getRepr(), shared);
250 }
251 return gr;
252 }
253
254 SPDocument *doc = gr->document;
255 SPObject *defs = doc->getDefs();
256
257 if ((gr->hasStops()) ||
258 (gr->hasPatches()) ||
260 (gr->parent != defs) ||
261 (gr->hrefcount > 1)) {
262
263 // we have to clone a fresh new private gradient for the given shared
264
265 // create an empty one
266 SPGradient *gr_new = sp_gradient_get_private_normalized(doc, shared, type);
267
268 // copy all the attributes to it
269 Inkscape::XML::Node *repr_new = gr_new->getRepr();
270 Inkscape::XML::Node *repr = gr->getRepr();
271 repr_new->setAttribute("gradientUnits", repr->attribute("gradientUnits"));
272 repr_new->setAttribute("gradientTransform", repr->attribute("gradientTransform"));
273 if (is<SPRadialGradient>(gr)) {
274 repr_new->setAttribute("cx", repr->attribute("cx"));
275 repr_new->setAttribute("cy", repr->attribute("cy"));
276 repr_new->setAttribute("fx", repr->attribute("fx"));
277 repr_new->setAttribute("fy", repr->attribute("fy"));
278 repr_new->setAttribute("r", repr->attribute("r" ));
279 repr_new->setAttribute("fr", repr->attribute("fr"));
280 repr_new->setAttribute("spreadMethod", repr->attribute("spreadMethod"));
281 } else if (is<SPLinearGradient>(gr)) {
282 repr_new->setAttribute("x1", repr->attribute("x1"));
283 repr_new->setAttribute("y1", repr->attribute("y1"));
284 repr_new->setAttribute("x2", repr->attribute("x2"));
285 repr_new->setAttribute("y2", repr->attribute("y2"));
286 repr_new->setAttribute("spreadMethod", repr->attribute("spreadMethod"));
287 } else { // Mesh
288 repr_new->setAttribute("x", repr->attribute("x"));
289 repr_new->setAttribute("y", repr->attribute("y"));
290 repr_new->setAttribute("type", repr->attribute("type"));
291
292 // We probably want a completely separate mesh gradient so
293 // copy the children and unset the link to the shared.
294 for ( Inkscape::XML::Node *child = repr->firstChild() ; child ; child = child->next() ) {
295 Inkscape::XML::Node *copy = child->duplicate(doc->getReprDoc());
296 repr_new->appendChild( copy );
297 Inkscape::GC::release( copy );
298 }
299 sp_gradient_repr_set_link(repr_new, nullptr);
300
301 // Need to generate SPObjects (which fills in node array and handle arrays).
302 gr->requestModified(SP_OBJECT_MODIFIED_FLAG);
304 }
305 return gr_new;
306 } else {
307 return gr;
308 }
309}
310
312{
313#ifdef SP_GR_VERBOSE
314 g_message("sp_gradient_fork_vector_if_necessary(%p)", gr);
315#endif
316 // Some people actually prefer their gradient vectors to be shared...
318 if (!prefs->getBool("/options/forkgradientvectors/value", true))
319 return gr;
320
321 if (gr->hrefcount > 1) {
322 SPDocument *doc = gr->document;
323 Inkscape::XML::Document *xml_doc = doc->getReprDoc();
324
325 Inkscape::XML::Node *repr = gr->getRepr()->duplicate(xml_doc);
326 doc->getDefs()->getRepr()->addChild(repr, nullptr);
327 SPGradient *gr_new = static_cast<SPGradient *>(doc->getObjectByRepr(repr));
328 gr_new = sp_gradient_ensure_vector_normalized (gr_new);
330 return gr_new;
331 }
332 return gr;
333}
334
339{
340#ifdef SP_GR_VERBOSE
341 g_message("sp_gradient_get_forked_vector_if_necessary(%p, %d)", gradient, force_vector);
342#endif
343 SPGradient *vector = gradient->getVector(force_vector);
344 vector = sp_gradient_fork_vector_if_necessary (vector);
345 if ( gradient != vector && gradient->ref->getObject() != vector ) {
346 sp_gradient_repr_set_link(gradient->getRepr(), vector);
347 }
348 return vector;
349}
350
351
358{
359#ifdef SP_GR_VERBOSE
360 g_message("sp_gradient_reset_to_userspace(%p, %p)", gr, item);
361#endif
362 Inkscape::XML::Node *repr = gr->getRepr();
363
364 // calculate the bbox of the item
366 Geom::OptRect bbox = item->visualBounds(); // we need "true" bbox without item_i2d_affine
367
368 if (!bbox)
369 return gr;
370
371 Geom::Coord const width = bbox->dimensions()[Geom::X];
372 Geom::Coord const height = bbox->dimensions()[Geom::Y];
373
374 Geom::Point const center = bbox->midpoint();
375
376 if (is<SPRadialGradient>(gr)) {
377 repr->setAttributeSvgDouble("cx", center[Geom::X]);
378 repr->setAttributeSvgDouble("cy", center[Geom::Y]);
379 repr->setAttributeSvgDouble("fx", center[Geom::X]);
380 repr->setAttributeSvgDouble("fy", center[Geom::Y]);
381 repr->setAttributeSvgDouble("r", width/2);
382
383 // we want it to be elliptic, not circular
384 Geom::Affine squeeze = Geom::Translate (-center) *
386 Geom::Translate (center);
387
388 gr->gradientTransform = squeeze;
390 } else if (is<SPLinearGradient>(gr)) {
391
392 // Assume horizontal gradient by default (as per SVG 1.1)
393 Geom::Point pStart = center - Geom::Point(width/2, 0);
394 Geom::Point pEnd = center + Geom::Point(width/2, 0);
395
396 // Get the preferred gradient angle from prefs
398 double angle = prefs->getDouble("/dialogs/gradienteditor/angle", 0.0);
399
400 if (angle != 0.0) {
401
402 Geom::Line grl(center, Geom::rad_from_deg(angle));
403 Geom::LineSegment bbl1(bbox->corner(0), bbox->corner(1));
404 Geom::LineSegment bbl2(bbox->corner(1), bbox->corner(2));
405 Geom::LineSegment bbl3(bbox->corner(2), bbox->corner(3));
406 Geom::LineSegment bbl4(bbox->corner(3), bbox->corner(0));
407
408 // Find where our gradient line intersects the bounding box.
409 if (!bbl1.isDegenerate() && intersection(bbl1, grl)) {
410 pStart = bbl1.pointAt((*intersection(bbl1, grl)).ta);
411 pEnd = bbl3.pointAt((*intersection(bbl3, grl)).ta);
412 if (intersection(bbl1, grl.ray(grl.angle()))) {
413 std::swap(pStart, pEnd);
414 }
415 } else if (!bbl2.isDegenerate() && intersection(bbl2, grl)) {
416 pStart = bbl2.pointAt((*intersection(bbl2, grl)).ta);
417 pEnd = bbl4.pointAt((*intersection(bbl4, grl)).ta);
418 if (intersection(bbl2, grl.ray(grl.angle()))) {
419 std::swap(pStart, pEnd);
420 }
421 }
422
423 }
424
425 repr->setAttributeSvgDouble("x1", pStart[Geom::X]);
426 repr->setAttributeSvgDouble("y1", pStart[Geom::Y]);
427 repr->setAttributeSvgDouble("x2", pEnd[Geom::X]);
428 repr->setAttributeSvgDouble("y2", pEnd[Geom::Y]);
429
430 } else {
431 // Mesh
432 // THIS IS BEING CALLED TWICE WHENEVER A NEW GRADIENT IS CREATED, WRITING HERE CAUSES PROBLEMS
433 // IN SPMeshNodeArray::create()
434 //repr->setAttributeSvgDouble("x", bbox->min()[Geom::X]);
435 //repr->setAttributeSvgDouble("y", bbox->min()[Geom::Y]);
436
437 // We don't create a shared array gradient.
438 auto mg = cast<SPMeshGradient>( gr );
439 mg->array.create( mg, item, bbox );
440 }
441
442 // set the gradientUnits
443 repr->setAttribute("gradientUnits", "userSpaceOnUse");
444
445 return gr;
446}
447
453{
454#ifdef SP_GR_VERBOSE
455 g_message("sp_gradient_convert_to_userspace(%p, %p, \"%s\")", gr, item, property);
456#endif
457 g_return_val_if_fail(gr, NULL);
458
459 if ( gr && gr->isSolid() ) {
460 return gr;
461 }
462
463 // First, fork it if it is shared
464 if (is<SPLinearGradient>(gr)) {
466 } else if (is<SPRadialGradient>(gr)) {
468 } else {
470 }
471
473
474 Inkscape::XML::Node *repr = gr->getRepr();
475
476 // calculate the bbox of the item
478 Geom::Affine bbox2user;
479 Geom::OptRect bbox = item->visualBounds(); // we need "true" bbox without item_i2d_affine
480 if ( bbox ) {
481 bbox2user = Geom::Affine(bbox->dimensions()[Geom::X], 0,
482 0, bbox->dimensions()[Geom::Y],
483 bbox->min()[Geom::X], bbox->min()[Geom::Y]);
484 } else {
485 // would be degenerate otherwise
486 bbox2user = Geom::identity();
487 }
488
489 /* skew is the additional transform, defined by the proportions of the item, that we need
490 * to apply to the gradient in order to work around this weird bit from SVG 1.1
491 * (http://www.w3.org/TR/SVG11/pservers.html#LinearGradients):
492 *
493 * When gradientUnits="objectBoundingBox" and gradientTransform is the identity
494 * matrix, the stripes of the linear gradient are perpendicular to the gradient
495 * vector in object bounding box space (i.e., the abstract coordinate system where
496 * (0,0) is at the top/left of the object bounding box and (1,1) is at the
497 * bottom/right of the object bounding box). When the object's bounding box is not
498 * square, the stripes that are conceptually perpendicular to the gradient vector
499 * within object bounding box space will render non-perpendicular relative to the
500 * gradient vector in user space due to application of the non-uniform scaling
501 * transformation from bounding box space to user space.
502 */
503 Geom::Affine skew = bbox2user;
504 double exp = skew.descrim();
505 skew[0] /= exp;
506 skew[1] /= exp;
507 skew[2] /= exp;
508 skew[3] /= exp;
509 skew[4] = 0;
510 skew[5] = 0;
511
512 // apply skew to the gradient
513 gr->gradientTransform = skew;
515
516 // Matrix to convert points to userspace coords; postmultiply by inverse of skew so
517 // as to cancel it out when it's applied to the gradient during rendering
518 Geom::Affine point_convert = bbox2user * skew.inverse();
519
520 if (is<SPLinearGradient>(gr)) {
521 auto lg = cast<SPLinearGradient>(gr);
522
523 Geom::Point p1_b = Geom::Point(lg->x1.computed, lg->y1.computed);
524 Geom::Point p2_b = Geom::Point(lg->x2.computed, lg->y2.computed);
525
526 Geom::Point p1_u = p1_b * point_convert;
527 Geom::Point p2_u = p2_b * point_convert;
528
529 repr->setAttributeSvgDouble("x1", p1_u[Geom::X]);
530 repr->setAttributeSvgDouble("y1", p1_u[Geom::Y]);
531 repr->setAttributeSvgDouble("x2", p2_u[Geom::X]);
532 repr->setAttributeSvgDouble("y2", p2_u[Geom::Y]);
533
534 // set the gradientUnits
535 repr->setAttribute("gradientUnits", "userSpaceOnUse");
536
537 } else if (is<SPRadialGradient>(gr)) {
538 auto rg = cast<SPRadialGradient>(gr);
539
540 // original points in the bbox coords
541 Geom::Point c_b = Geom::Point(rg->cx.computed, rg->cy.computed);
542 Geom::Point f_b = Geom::Point(rg->fx.computed, rg->fy.computed);
543 double r_b = rg->r.computed;
544
545 // converted points in userspace coords
546 Geom::Point c_u = c_b * point_convert;
547 Geom::Point f_u = f_b * point_convert;
548 double r_u = r_b * point_convert.descrim();
549
550 repr->setAttributeSvgDouble("cx", c_u[Geom::X]);
551 repr->setAttributeSvgDouble("cy", c_u[Geom::Y]);
552 repr->setAttributeSvgDouble("fx", f_u[Geom::X]);
553 repr->setAttributeSvgDouble("fy", f_u[Geom::Y]);
554 repr->setAttributeSvgDouble("r", r_u);
555
556 // set the gradientUnits
557 repr->setAttribute("gradientUnits", "userSpaceOnUse");
558
559 } else {
560 std::cerr << "sp_gradient_convert_to_userspace: Conversion of mesh to userspace not implemented" << std::endl;
561 }
562 }
563
564 // apply the gradient to the item (may be necessary if we forked it); not recursive
565 // generally because grouped items will be taken care of later (we're being called
566 // from sp_item_adjust_paint_recursive); however text and all its children should all
567 // refer to one gradient, hence the recursive call for text (because we can't/don't
568 // want to access tspans and set gradients on them separately)
569 if (is<SPText>(item)) {
570 sp_style_set_property_url(item, property, gr, true);
571 } else {
572 sp_style_set_property_url(item, property, gr, false);
573 }
574
575 return gr;
576}
577
579{
580#ifdef SP_GR_VERBOSE
581 g_message("sp_gradient_transform_multiply(%p, , %d)", gradient, set);
582#endif
583 if (set) {
584 gradient->gradientTransform = postmul;
585 } else {
586 gradient->gradientTransform *= postmul; // fixme: get gradient transform by climbing to hrefs?
587 }
588 gradient->gradientTransform_set = TRUE;
589
591 gradient->setAttributeOrRemoveIfEmpty("gradientTransform", c);
592}
593
595{
596 SPStyle *style = item->style;
597 SPGradient *gradient = nullptr;
598
599 switch (fill_or_stroke)
600 {
602 if (style && (style->fill.isPaintserver())) {
604 if ( is<SPGradient>(server) ) {
605 gradient = cast<SPGradient>(server);
606 }
607 }
608 break;
610 if (style && (style->stroke.isPaintserver())) {
612 if ( is<SPGradient>(server) ) {
613 gradient = cast<SPGradient>(server);
614 }
615 }
616 break;
617 }
618
619 return gradient;
620}
621
623{
624 for (SPStop *stop = gradient->getFirstStop(); stop != nullptr; stop = stop->getNextStop()) {
625 if (stop->getNextStop() == nullptr)
626 return stop;
627 }
628 return nullptr;
629}
630
631std::pair<SPStop*, SPStop*> sp_get_before_after_stops(SPStop* stop) {
632 SPStop* before = nullptr;
633 SPStop* after = nullptr;
634
635 if (stop) {
636 before = stop->getPrevStop();
637 after = stop->getNextStop();
638 }
639
640 return std::make_pair(before, after);
641}
642
643static std::pair<SPStop*, SPStop*> get_before_after_stops(SPGradient* gradient, double offset) {
644 SPStop* before = nullptr;
645 SPStop* after = nullptr;
646
647 SPStop* stop = gradient->getFirstStop();
648 while (stop && stop->offset < offset) {
649 before = stop;
650 stop = stop->getNextStop();
651 }
652
653 if (stop && stop->offset > offset) {
654 after = stop;
655 }
656
657 return std::make_pair(before, after);
658}
659
661 if (!gradient) return 0;
662
663 guint n = 0;
664 for (SPStop* stop = gradient->getFirstStop(); stop != nullptr; stop = stop->getNextStop()) {
665 if (stop == target) {
666 return n;
667 }
668 n++;
669 }
670 return n;
671}
672
673
675 SPStop* stop = gradient->getFirstStop();
676 if (!stop) return nullptr;
677
678 for (guint i = 0; i < index; ++i) {
679 if (!stop) return nullptr;
680
681 stop = stop->getNextStop();
682 }
683
684 return stop;
685}
686
687
688SPStop *sp_get_stop_i(SPGradient *gradient, guint stop_i)
689{
690 SPStop *stop = gradient->getFirstStop();
691 if (!stop) {
692 return nullptr;
693 }
694
695 // if this is valid but weird gradient without an offset-zero stop element,
696 // inkscape has created a handle for the start of gradient anyway,
697 // so when it asks for stop N that corresponds to stop element N-1
698 if (stop->offset != 0)
699 {
700 stop_i--;
701 }
702
703 for (guint i = 0; i < stop_i; i++) {
704 if (!stop) {
705 return nullptr;
706 }
707 stop = stop->getNextStop();
708 }
709
710 return stop;
711}
712
713void sp_repr_set_css_double(Inkscape::XML::Node* node, const char* key, double value) {
714 if (node) {
716 }
717}
718
719SPStop *sp_vector_add_stop(SPGradient *vector, SPStop* prev_stop, SPStop* next_stop, gfloat offset)
720{
721#ifdef SP_GR_VERBOSE
722 g_message("sp_vector_add_stop(%p, %p, %p, %f)", vector, prev_stop, next_stop, offset);
723#endif
724 SPStop* newstop = nullptr;
725 // this function doesn't deal with empty gradients
726 if (!prev_stop && !next_stop) return newstop;
727
728 // This function completely breaks CMYK gradients.
729 Color cnew(0x000000ff); // new color
730 Inkscape::XML::Node *new_stop_repr = nullptr;
731
732 if (!prev_stop || !next_stop) {
733 // inserting stop past next or before previous is supported
734 SPStop* stop = prev_stop ? prev_stop : next_stop;
735 auto repr = stop->getRepr();
736 new_stop_repr = repr->duplicate(vector->getRepr()->document());
737 vector->getRepr()->addChild(new_stop_repr, prev_stop ? repr : nullptr);
738
739 cnew = stop->getColor();
740 }
741 else {
742 auto repr = prev_stop->getRepr();
743 new_stop_repr = repr->duplicate(vector->getRepr()->document());
744 vector->getRepr()->addChild(new_stop_repr, repr);
745 cnew = prev_stop->getColor().averaged(next_stop->getColor(), (offset - prev_stop->offset) / (next_stop->offset - prev_stop->offset));
746 }
747
748 newstop = reinterpret_cast<SPStop *>(vector->document->getObjectByRepr(new_stop_repr));
749 newstop->offset = offset;
750 newstop->getRepr()->setAttributeCssDouble("offset", (double)offset);
751 newstop->setColor(cnew);
752 Inkscape::GC::release(new_stop_repr);
753
754 return newstop;
755}
756
757// delete gradient's stop
759
760 if (!stop || !gradient) {
761 return;
762 }
763
764 if (gradient->getStopCount() > 2) { // 2 is the minimum
765 gradient->getRepr()->removeChild(stop->getRepr());
766 DocumentUndo::done(gradient->document, _("Delete gradient stop"), INKSCAPE_ICON("color-gradient"));
767 }
768}
769
770// make gradient well-formed if needed; from gradient-vector.cpp
771static bool verify_grad(SPGradient* gradient) {
772 bool modified = false;
773 int i = 0;
774 SPStop *stop = nullptr;
775 /* count stops */
776 for (auto& ochild: gradient->children) {
777 if (is<SPStop>(&ochild)) {
778 i++;
779 stop = cast<SPStop>(&ochild);
780 }
781 }
782
784 xml_doc = gradient->getRepr()->document();
785
786 if (i < 1) {
788
789 child = xml_doc->createElement("svg:stop");
790 sp_repr_set_css_double(child, "offset", 0.0);
791 SPStop::setColorRepr(child, Colors::Color(0x000000ff));
792 gradient->getRepr()->addChild(child, nullptr);
794
795 child = xml_doc->createElement("svg:stop");
796 sp_repr_set_css_double(child, "offset", 1.0);
797 SPStop::setColorRepr(child, Colors::Color(0x000000ff));
798 gradient->getRepr()->addChild(child, nullptr);
800 modified = true;
801 }
802 else if (i < 2) {
803 sp_repr_set_css_double(stop->getRepr(), "offset", 0.0);
804 Inkscape::XML::Node *child = stop->getRepr()->duplicate(gradient->getRepr()->document());
805 sp_repr_set_css_double(child, "offset", 1.0);
806 gradient->getRepr()->addChild(child, stop->getRepr());
808 modified = true;
809 }
810
811 return modified;
812}
813
814// add new stop to a gradient; function lifted from gradient-vector.cpp
816 if (!gradient || !current) return nullptr;
817
818 if (verify_grad(gradient)) {
819 // gradient has been fixed by adding stop(s), don't insert another one
820 return nullptr;
821 }
822
823 SPStop *stop = current;
824 Inkscape::XML::Node *new_stop_repr = nullptr;
825 SPStop *next = stop->getNextStop();
826
827 if (next == nullptr) {
828 SPStop *prev = stop->getPrevStop();
829 if (prev != nullptr) {
830 next = stop;
831 stop = prev;
832 }
833 }
834
835 if (next != nullptr) {
836 new_stop_repr = stop->getRepr()->duplicate(gradient->getRepr()->document());
837 gradient->getRepr()->addChild(new_stop_repr, stop->getRepr());
838 } else {
839 next = stop;
840 new_stop_repr = stop->getPrevStop()->getRepr()->duplicate(gradient->getRepr()->document());
841 gradient->getRepr()->addChild(new_stop_repr, stop->getPrevStop()->getRepr());
842 }
843
844 SPStop *newstop = reinterpret_cast<SPStop *>(gradient->document->getObjectByRepr(new_stop_repr));
845
846 newstop->offset = (stop->offset + next->offset) * 0.5 ;
847
848 newstop->setColor(stop->getColor().averaged(next->getColor()));
849 sp_repr_set_css_double(newstop->getRepr(), "offset", (double)newstop->offset);
850 Inkscape::GC::release(new_stop_repr);
851 DocumentUndo::done(gradient->document, _("Add gradient stop"), INKSCAPE_ICON("color-gradient"));
852
853 return newstop;
854}
855
857 if (!gradient) return nullptr;
858
859 verify_grad(gradient);
860
861 // find stops before and after given offset
862
863 std::pair<SPStop*, SPStop*> stops = get_before_after_stops(gradient, offset);
864
865 if (stops.first || stops.second) {
866 auto stop = sp_vector_add_stop(gradient, stops.first, stops.second, offset);
867 if (stop) {
868 DocumentUndo::done(gradient->document, _("Add gradient stop"), INKSCAPE_ICON("color-gradient"));
869 }
870 return stop;
871 }
872 else {
873 return nullptr;
874 }
875}
876
877void sp_set_gradient_stop_color(SPDocument* document, SPStop* stop, Color const &color) {
878 sp_repr_set_css_double(stop->getRepr(), "offset", stop->offset);
879 stop->setColor(color);
880 DocumentUndo::maybeDone(document, "gradient:stop:color", _("Change gradient stop color"), INKSCAPE_ICON("color-gradient"));
881}
882
884 SPGradient *gradient = getGradient(item, fill_or_stroke);
885
886 if (!gradient) {
887 return nullptr;
888 }
889
890 if (is<SPLinearGradient>(gradient) || is<SPRadialGradient>(gradient) ) {
891
892 SPGradient *vector = gradient->getVector();
893
894 if (!vector) // orphan!
895 return nullptr;
896
897 switch (point_type) {
898 case POINT_LG_BEGIN:
899 case POINT_RG_CENTER:
900 case POINT_RG_FOCUS:
901 return vector->getFirstStop();
902
903 case POINT_LG_END:
904 case POINT_RG_R1:
905 case POINT_RG_R2:
906 return sp_last_stop (vector);
907
908 case POINT_LG_MID:
909 case POINT_RG_MID1:
910 case POINT_RG_MID2:
911 return sp_get_stop_i (vector, point_i);
912
913 default:
914 g_warning( "Bad linear/radial gradient handle type" );
915 break;
916 }
917 }
918 return nullptr;
919}
920
922{
923 SPGradient *gradient = getGradient(item, fill_or_stroke);
924
925 if (!gradient)
926 return Colors::Color(0x000000ff);
927
928 if (is<SPLinearGradient>(gradient) || is<SPRadialGradient>(gradient) ) {
929
930 SPGradient *vector = gradient->getVector();
931
932 if (!vector) // orphan!
933 return Colors::Color(0x000000ff);
934
935 switch (point_type) {
936 case POINT_LG_BEGIN:
937 case POINT_RG_CENTER:
938 case POINT_RG_FOCUS:
939 {
940 SPStop *first = vector->getFirstStop();
941 if (first) {
942 return first->getColor();
943 }
944 }
945 break;
946
947 case POINT_LG_END:
948 case POINT_RG_R1:
949 case POINT_RG_R2:
950 {
951 SPStop *last = sp_last_stop (vector);
952 if (last) {
953 return last->getColor();
954 }
955 }
956 break;
957
958 case POINT_LG_MID:
959 case POINT_RG_MID1:
960 case POINT_RG_MID2:
961 {
962 SPStop *stopi = sp_get_stop_i (vector, point_i);
963 if (stopi) {
964 return stopi->getColor();
965 }
966 }
967 break;
968
969 default:
970 g_warning( "Bad linear/radial gradient handle type" );
971 break;
972 }
973 } else if (is<SPMeshGradient>(gradient)) {
974
975 // Mesh gradient
976 auto mg = cast<SPMeshGradient>(gradient);
977
978 switch (point_type) {
979 case POINT_MG_CORNER: {
980 if (point_i >= mg->array.corners.size()) {
981 return Colors::Color(0x000000ff);
982 }
983 SPMeshNode const* cornerpoint = mg->array.corners[ point_i ];
984
985 if (cornerpoint) {
986 return *cornerpoint->color;
987 }
988 break;
989 }
990
991 case POINT_MG_HANDLE:
992 case POINT_MG_TENSOR:
993 {
994 // Do nothing. Handles and tensors don't have color
995 break;
996 }
997
998 default:
999 g_warning( "Bad mesh handle type" );
1000 }
1001 }
1002 return Colors::Color(0x000000ff);
1003}
1004
1006{
1007#ifdef SP_GR_VERBOSE
1008 g_message("sp_item_gradient_stop_set_style(%p, %d, %d, %d, %p)", item, point_type, point_i, fill_or_stroke, stop);
1009#endif
1010 SPGradient *gradient = getGradient(item, fill_or_stroke);
1011
1012 if (!gradient)
1013 return;
1014
1015 if (is<SPLinearGradient>(gradient) || is<SPRadialGradient>(gradient) ) {
1016
1017 SPGradient *vector = gradient->getVector();
1018
1019 if (!vector) // orphan!
1020 return;
1021
1022 vector = sp_gradient_fork_vector_if_necessary (vector);
1023 if ( gradient != vector && gradient->ref->getObject() != vector ) {
1024 sp_gradient_repr_set_link(gradient->getRepr(), vector);
1025 }
1026
1027 switch (point_type) {
1028 case POINT_LG_BEGIN:
1029 case POINT_RG_CENTER:
1030 case POINT_RG_FOCUS:
1031 {
1032 SPStop *first = vector->getFirstStop();
1033 if (first) {
1034 sp_repr_css_change(first->getRepr(), stop, "style");
1035 }
1036 }
1037 break;
1038
1039 case POINT_LG_END:
1040 case POINT_RG_R1:
1041 case POINT_RG_R2:
1042 {
1043 SPStop *last = sp_last_stop (vector);
1044 if (last) {
1045 sp_repr_css_change(last->getRepr(), stop, "style");
1046 }
1047 }
1048 break;
1049
1050 case POINT_LG_MID:
1051 case POINT_RG_MID1:
1052 case POINT_RG_MID2:
1053 {
1054 SPStop *stopi = sp_get_stop_i (vector, point_i);
1055 if (stopi) {
1056 sp_repr_css_change(stopi->getRepr(), stop, "style");
1057 }
1058 }
1059 break;
1060
1061 default:
1062 g_warning( "Bad linear/radial gradient handle type" );
1063 break;
1064 }
1065 } else {
1066
1067 // Mesh gradient
1068 auto mg = cast<SPMeshGradient>(gradient);
1069
1070 switch (point_type) {
1071 case POINT_MG_CORNER: {
1072
1073 // Update mesh array (which is not updated automatically when stop is changed?)
1074 gchar const* color_str = sp_repr_css_property(stop, "stop-color", nullptr);
1075 if (auto color = Color::parse(color_str)) {
1076 color->addOpacity(sp_repr_css_double_property(stop, "stop-opacity", 1.0));
1077 if (*color != mg->array.corners[point_i]->color) {
1078 mg->array.corners[ point_i ]->color = *color;
1079 // Update stop
1080 SPStop *stopi = mg->array.corners[ point_i ]->stop;
1081 if (stopi) {
1082 sp_repr_css_change(stopi->getRepr(), stop, "style");
1083 } else {
1084 std::cerr << "sp_item_gradient_stop_set_style: null stopi" << std::endl;
1085 }
1086 }
1087 }
1088 break;
1089 }
1090
1091 case POINT_MG_HANDLE:
1092 case POINT_MG_TENSOR:
1093 {
1094 // Do nothing. Handles and tensors don't have colors.
1095 break;
1096 }
1097
1098 default:
1099 g_warning( "Bad mesh handle type" );
1100 }
1101 }
1102}
1103
1105{
1106#ifdef SP_GR_VERBOSE
1107 g_message("sp_item_gradient_reverse_vector(%p, %d)", item, fill_or_stroke);
1108#endif
1109 SPGradient *gradient = getGradient(item, fill_or_stroke);
1111}
1112
1114 if (!gradient)
1115 return;
1116
1117 SPGradient *vector = gradient->getVector();
1118 if (!vector) // orphan!
1119 return;
1120
1121 vector = sp_gradient_fork_vector_if_necessary (vector);
1122 if ( gradient != vector && gradient->ref->getObject() != vector ) {
1123 sp_gradient_repr_set_link(gradient->getRepr(), vector);
1124 }
1125
1126 std::vector<SPObject *> child_objects;
1127 std::vector<Inkscape::XML::Node *>child_reprs;
1128 std::vector<double> offsets;
1129 double offset;
1130 for (auto& child: vector->children) {
1131 child_reprs.push_back(child.getRepr());
1132 child_objects.push_back(&child);
1133 offset = child.getRepr()->getAttributeDouble("offset", 0);
1134 offsets.push_back(offset);
1135 }
1136
1137 std::vector<Inkscape::XML::Node *> child_copies;
1138 for (auto repr:child_reprs) {
1139 Inkscape::XML::Document *xml_doc = vector->getRepr()->document();
1140 child_copies.push_back(repr->duplicate(xml_doc));
1141 }
1142
1143
1144 for (auto i:child_objects) {
1145 i->deleteObject();
1146 }
1147
1148 std::vector<double>::reverse_iterator o_it = offsets.rbegin();
1149 for (auto c_it = child_copies.rbegin(); c_it != child_copies.rend(); ++c_it, ++o_it) {
1150 vector->appendChildRepr(*c_it);
1151 (*c_it)->setAttributeSvgDouble("offset", 1 - *o_it);
1152 Inkscape::GC::release(*c_it);
1153 }
1154}
1155
1157{
1158#ifdef SP_GR_VERBOSE
1159 g_message("sp_item_gradient_invert_vector_color(%p, %d)", item, fill_or_stroke);
1160#endif
1161 SPGradient *gradient = getGradient(item, fill_or_stroke);
1162 if (!gradient)
1163 return;
1164
1165 SPGradient *vector = gradient->getVector();
1166 if (!vector) // orphan!
1167 return;
1168
1169 vector = sp_gradient_fork_vector_if_necessary (vector);
1170 if ( gradient != vector && gradient->ref->getObject() != vector ) {
1171 sp_gradient_repr_set_link(gradient->getRepr(), vector);
1172 }
1173
1174 for (auto &child: vector->children) {
1175 if (auto stop = cast<SPStop>(&child)) {
1176 auto color = stop->getColor();
1177 color.invert();
1178 stop->setColor(color);
1179 }
1180 }
1181}
1182
1183// HACK: linear and radial gradients may have first and/or last stops moved from their default positions
1184// of 0 and 1 respectively; this is not what gradient tool was built to handle; instead of making extensive
1185// changes to try to fix it, this hack just makes sure that midpoint draggers don't move to the true 0/1 limits;
1186// with that, code relying on sp_get_stop_i will work correctly
1188 const double EPS = 0.0001;
1189
1190 if (offset <= 0) {
1191 offset = EPS;
1192 }
1193 else if (offset >= 1) {
1194 offset = 1 - EPS;
1195 }
1196
1197 return offset;
1198}
1199
1204void sp_item_gradient_set_coords(SPItem *item, GrPointType point_type, guint point_i, Geom::Point p_w, Inkscape::PaintTarget fill_or_stroke, bool write_repr, bool scale)
1205{
1206#ifdef SP_GR_VERBOSE
1207 g_message("sp_item_gradient_set_coords(%p, %d, %d, (%f, %f), ...)", item, point_type, point_i, p_w[Geom::X], p_w[Geom::Y] );
1208#endif
1209 SPGradient *gradient = getGradient(item, fill_or_stroke);
1210
1211 if (!gradient)
1212 return;
1213
1214 // Needed only if units are set to SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX
1215 gradient = sp_gradient_convert_to_userspace(gradient, item, (fill_or_stroke == Inkscape::FOR_FILL) ? "fill" : "stroke");
1216
1217 Geom::Affine i2d (item->i2dt_affine ());
1218 Geom::Point p = p_w * i2d.inverse();
1219 p *= (gradient->gradientTransform).inverse();
1220 // now p is in gradient's original coordinates
1221
1222 Inkscape::XML::Node *repr = gradient->getRepr();
1223
1224 if (is<SPLinearGradient>(gradient)) {
1225 auto lg = cast<SPLinearGradient>(gradient);
1226 switch (point_type) {
1227 case POINT_LG_BEGIN:
1228 if (scale) {
1229 lg->x2.computed += (lg->x1.computed - p[Geom::X]);
1230 lg->y2.computed += (lg->y1.computed - p[Geom::Y]);
1231 }
1232 lg->x1.computed = p[Geom::X];
1233 lg->y1.computed = p[Geom::Y];
1234 if (write_repr) {
1235 if (scale) {
1236 repr->setAttributeSvgDouble("x2", lg->x2.computed);
1237 repr->setAttributeSvgDouble("y2", lg->y2.computed);
1238 }
1239 repr->setAttributeSvgDouble("x1", lg->x1.computed);
1240 repr->setAttributeSvgDouble("y1", lg->y1.computed);
1241 } else {
1242 gradient->requestModified(SP_OBJECT_MODIFIED_FLAG);
1243 }
1244 break;
1245 case POINT_LG_END:
1246 if (scale) {
1247 lg->x1.computed += (lg->x2.computed - p[Geom::X]);
1248 lg->y1.computed += (lg->y2.computed - p[Geom::Y]);
1249 }
1250 lg->x2.computed = p[Geom::X];
1251 lg->y2.computed = p[Geom::Y];
1252 if (write_repr) {
1253 if (scale) {
1254 repr->setAttributeSvgDouble("x1", lg->x1.computed);
1255 repr->setAttributeSvgDouble("y1", lg->y1.computed);
1256 }
1257 repr->setAttributeSvgDouble("x2", lg->x2.computed);
1258 repr->setAttributeSvgDouble("y2", lg->y2.computed);
1259 } else {
1260 gradient->requestModified(SP_OBJECT_MODIFIED_FLAG);
1261 }
1262 break;
1263 case POINT_LG_MID:
1264 {
1265 // using X-coordinates only to determine the offset, assuming p has been snapped to the vector from begin to end.
1266 Geom::Point begin(lg->x1.computed, lg->y1.computed);
1267 Geom::Point end(lg->x2.computed, lg->y2.computed);
1268 double offset = Geom::LineSegment(begin, end).nearestTime(p);
1271 lg->ensureVector();
1272 lg->vector.stops.at(point_i).offset = offset;
1273 if (SPStop* stopi = sp_get_stop_i(vector, point_i)) {
1274 stopi->offset = offset;
1275 if (write_repr) {
1276 stopi->getRepr()->setAttributeCssDouble("offset", stopi->offset);
1277 } else {
1278 stopi->requestModified(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG);
1279 }
1280 }
1281 }
1282 break;
1283 default:
1284 g_warning( "Bad linear gradient handle type" );
1285 break;
1286 }
1287 } else if (is<SPRadialGradient>(gradient)) {
1288 auto rg = cast<SPRadialGradient>(gradient);
1289 Geom::Point c (rg->cx.computed, rg->cy.computed);
1290 Geom::Point c_w = c * gradient->gradientTransform * i2d; // now in desktop coords
1291 if ((point_type == POINT_RG_R1 || point_type == POINT_RG_R2) && Geom::L2 (p_w - c_w) < 1e-3) {
1292 // prevent setting a radius too close to the center
1293 return;
1294 }
1295 Geom::Affine new_transform;
1296 bool transform_set = false;
1297
1298 switch (point_type) {
1299 case POINT_RG_CENTER:
1300 rg->fx.computed = p[Geom::X] + (rg->fx.computed - rg->cx.computed);
1301 rg->fy.computed = p[Geom::Y] + (rg->fy.computed - rg->cy.computed);
1302 rg->cx.computed = p[Geom::X];
1303 rg->cy.computed = p[Geom::Y];
1304 if (write_repr) {
1305 repr->setAttributeSvgDouble("fx", rg->fx.computed);
1306 repr->setAttributeSvgDouble("fy", rg->fy.computed);
1307 repr->setAttributeSvgDouble("cx", rg->cx.computed);
1308 repr->setAttributeSvgDouble("cy", rg->cy.computed);
1309 } else {
1310 gradient->requestModified(SP_OBJECT_MODIFIED_FLAG);
1311 }
1312 break;
1313 case POINT_RG_FOCUS:
1314 rg->fx.computed = p[Geom::X];
1315 rg->fy.computed = p[Geom::Y];
1316 if (write_repr) {
1317 repr->setAttributeSvgDouble("fx", rg->fx.computed);
1318 repr->setAttributeSvgDouble("fy", rg->fy.computed);
1319 } else {
1320 gradient->requestModified(SP_OBJECT_MODIFIED_FLAG);
1321 }
1322 break;
1323 case POINT_RG_R1:
1324 {
1325 Geom::Point r1_w = (c + Geom::Point(rg->r.computed, 0)) * gradient->gradientTransform * i2d;
1326 double r1_angle = Geom::atan2(r1_w - c_w);
1327 double move_angle = Geom::atan2(p_w - c_w) - r1_angle;
1328 double move_stretch = Geom::L2(p_w - c_w) / Geom::L2(r1_w - c_w);
1329
1330 Geom::Affine move = Geom::Affine (Geom::Translate (-c_w)) *
1331 Geom::Affine (Geom::Rotate(-r1_angle)) *
1332 Geom::Affine (Geom::Scale(move_stretch, scale? move_stretch : 1)) *
1333 Geom::Affine (Geom::Rotate(r1_angle)) *
1334 Geom::Affine (Geom::Rotate(move_angle)) *
1336
1337 new_transform = gradient->gradientTransform * i2d * move * i2d.inverse();
1338 transform_set = true;
1339
1340 break;
1341 }
1342 case POINT_RG_R2:
1343 {
1344 Geom::Point r2_w = (c + Geom::Point(0, -rg->r.computed)) * gradient->gradientTransform * i2d;
1345 double r2_angle = Geom::atan2(r2_w - c_w);
1346 double move_angle = Geom::atan2(p_w - c_w) - r2_angle;
1347 double move_stretch = Geom::L2(p_w - c_w) / Geom::L2(r2_w - c_w);
1348
1349 Geom::Affine move = Geom::Affine (Geom::Translate (-c_w)) *
1350 Geom::Affine (Geom::Rotate(-r2_angle)) *
1351 Geom::Affine (Geom::Scale(move_stretch, scale? move_stretch : 1)) *
1352 Geom::Affine (Geom::Rotate(r2_angle)) *
1353 Geom::Affine (Geom::Rotate(move_angle)) *
1355
1356 new_transform = gradient->gradientTransform * i2d * move * i2d.inverse();
1357 transform_set = true;
1358
1359 break;
1360 }
1361 case POINT_RG_MID1:
1362 {
1363 Geom::Point start = Geom::Point (rg->cx.computed, rg->cy.computed);
1364 Geom::Point end = Geom::Point (rg->cx.computed + rg->r.computed, rg->cy.computed);
1368 rg->ensureVector();
1369 rg->vector.stops.at(point_i).offset = offset;
1370 if (SPStop* stopi = sp_get_stop_i(vector, point_i)) {
1371 stopi->offset = offset;
1372 if (write_repr) {
1373 stopi->getRepr()->setAttributeCssDouble("offset", stopi->offset);
1374 } else {
1375 stopi->requestModified(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG);
1376 }
1377 }
1378 break;
1379 }
1380 case POINT_RG_MID2:
1381 {
1382 Geom::Point start = Geom::Point (rg->cx.computed, rg->cy.computed);
1383 Geom::Point end = Geom::Point (rg->cx.computed, rg->cy.computed - rg->r.computed);
1387 rg->ensureVector();
1388 rg->vector.stops.at(point_i).offset = offset;
1389 if (SPStop* stopi = sp_get_stop_i(vector, point_i)) {
1390 stopi->offset = offset;
1391 if (write_repr) {
1392 stopi->getRepr()->setAttributeCssDouble("offset", stopi->offset);
1393 } else {
1394 stopi->requestModified(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG);
1395 }
1396 }
1397 break;
1398 }
1399 default:
1400 g_warning( "Bad radial gradient handle type" );
1401 break;
1402 }
1403
1404 if (transform_set) {
1405 gradient->gradientTransform = new_transform;
1406 gradient->gradientTransform_set = TRUE;
1407 if (write_repr) {
1408 auto s = sp_svg_transform_write(gradient->gradientTransform);
1409 gradient->setAttributeOrRemoveIfEmpty("gradientTransform", s);
1410 } else {
1411 gradient->requestModified(SP_OBJECT_MODIFIED_FLAG);
1412 }
1413 }
1414 } else if (is<SPMeshGradient>(gradient)) {
1415 auto mg = cast<SPMeshGradient>(gradient);
1416 //Geom::Affine new_transform;
1417 //bool transform_set = false;
1418
1419 switch (point_type) {
1420 case POINT_MG_CORNER:
1421 {
1422 if (point_i < mg->array.corners.size()) {
1423 mg->array.corners[ point_i ]->p = p;
1424 // Handles are moved in gradient-drag.cpp
1425 gradient->requestModified(SP_OBJECT_MODIFIED_FLAG);
1426 } else {
1427 std::cerr << "sp_item_gradient_set_coords: bad point number" << std::endl;
1428 }
1429 break;
1430 }
1431
1432 case POINT_MG_HANDLE: {
1433 if (point_i < mg->array.handles.size()) {
1434 mg->array.handles[ point_i ]->p = p;
1435 gradient->requestModified(SP_OBJECT_MODIFIED_FLAG);
1436 } else {
1437 std::cerr << "sp_item_gradient_set_coords: bad point number" << std::endl;
1438 }
1439 break;
1440 }
1441
1442 case POINT_MG_TENSOR: {
1443 if (point_i < mg->array.tensors.size()) {
1444 mg->array.tensors[ point_i ]->p = p;
1445 gradient->requestModified(SP_OBJECT_MODIFIED_FLAG);
1446 } else {
1447 std::cerr << "sp_item_gradient_set_coords: bad point number" << std::endl;
1448 }
1449 break;
1450 }
1451
1452 default:
1453 g_warning( "Bad mesh handle type" );
1454 }
1455 if( write_repr ) {
1456 mg->array.write( mg );
1457 }
1458 }
1459
1460}
1461
1463{
1464 SPGradient *gradient = getGradient(item, fill_or_stroke);
1465
1466 if (gradient) {
1467 return gradient->getVector();
1468 }
1469 return nullptr;
1470}
1471
1473{
1475 SPGradient *gradient = getGradient(item, fill_or_stroke);
1476
1477 if (gradient) {
1478 spread = gradient->fetchSpread();
1479 }
1480 return spread;
1481}
1482
1483
1489{
1490 SPGradient *gradient = getGradient(item, fill_or_stroke);
1491#ifdef SP_GR_VERBOSE
1492 g_message("getGradientCoords(%p, %d, %d, %d, %p)", item, point_type, point_i, fill_or_stroke, gradient);
1493#endif
1494
1495 Geom::Point p (0, 0);
1496
1497 if (!gradient)
1498 return p;
1499
1500 if (is<SPLinearGradient>(gradient)) {
1501 auto lg = cast<SPLinearGradient>(gradient);
1502 switch (point_type) {
1503 case POINT_LG_BEGIN:
1504 p = Geom::Point (lg->x1.computed, lg->y1.computed);
1505 break;
1506 case POINT_LG_END:
1507 p = Geom::Point (lg->x2.computed, lg->y2.computed);
1508 break;
1509 case POINT_LG_MID:
1510 {
1511 if (lg->vector.stops.size() < point_i) {
1512 g_message("POINT_LG_MID bug trigger, see LP bug #453067");
1513 break;
1514 }
1515 gdouble offset = lg->vector.stops.at(point_i).offset;
1516 p = (1-offset) * Geom::Point(lg->x1.computed, lg->y1.computed) + offset * Geom::Point(lg->x2.computed, lg->y2.computed);
1517 }
1518 break;
1519 default:
1520 g_warning( "Bad linear gradient handle type" );
1521 break;
1522 }
1523 } else if (is<SPRadialGradient>(gradient)) {
1524 auto rg = cast<SPRadialGradient>(gradient);
1525 switch (point_type) {
1526 case POINT_RG_CENTER:
1527 p = Geom::Point (rg->cx.computed, rg->cy.computed);
1528 break;
1529 case POINT_RG_FOCUS:
1530 p = Geom::Point (rg->fx.computed, rg->fy.computed);
1531 break;
1532 case POINT_RG_R1:
1533 p = Geom::Point (rg->cx.computed + rg->r.computed, rg->cy.computed);
1534 break;
1535 case POINT_RG_R2:
1536 p = Geom::Point (rg->cx.computed, rg->cy.computed - rg->r.computed);
1537 break;
1538 case POINT_RG_MID1:
1539 {
1540 if (rg->vector.stops.size() < point_i) {
1541 g_message("POINT_RG_MID1 bug trigger, see LP bug #453067");
1542 break;
1543 }
1544 gdouble offset = rg->vector.stops.at(point_i).offset;
1545 p = (1-offset) * Geom::Point (rg->cx.computed, rg->cy.computed) + offset * Geom::Point(rg->cx.computed + rg->r.computed, rg->cy.computed);
1546 }
1547 break;
1548 case POINT_RG_MID2:
1549 {
1550 if (rg->vector.stops.size() < point_i) {
1551 g_message("POINT_RG_MID2 bug trigger, see LP bug #453067");
1552 break;
1553 }
1554 gdouble offset = rg->vector.stops.at(point_i).offset;
1555 p = (1-offset) * Geom::Point (rg->cx.computed, rg->cy.computed) + offset * Geom::Point(rg->cx.computed, rg->cy.computed - rg->r.computed);
1556 }
1557 break;
1558 default:
1559 g_warning( "Bad radial gradient handle type" );
1560 break;
1561 }
1562 } else if (is<SPMeshGradient>(gradient)) {
1563 auto mg = cast<SPMeshGradient>(gradient);
1564 switch (point_type) {
1565
1566 case POINT_MG_CORNER:
1567 p = mg->array.corners[ point_i ]->p;
1568 break;
1569
1570 case POINT_MG_HANDLE: {
1571 p = mg->array.handles[ point_i ]->p;
1572 break;
1573 }
1574
1575 case POINT_MG_TENSOR: {
1576 p = mg->array.tensors[ point_i ]->p;
1577 break;
1578 }
1579
1580 default:
1581 g_warning( "Bad mesh handle type" );
1582 }
1583 }
1584
1585
1586 if (gradient->getUnits() == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) {
1588 Geom::OptRect bbox = item->visualBounds(); // we need "true" bbox without item_i2d_affine
1589 if (bbox) {
1590 p *= Geom::Affine(bbox->dimensions()[Geom::X], 0,
1591 0, bbox->dimensions()[Geom::Y],
1592 bbox->min()[Geom::X], bbox->min()[Geom::Y]);
1593 }
1594 }
1596 return p;
1597}
1598
1606{
1607#ifdef SP_GR_VERBOSE
1608 g_message("sp_item_set_gradient(%p, %p, %d, %d)", item, gr, type, fill_or_stroke);
1609#endif
1610 g_return_val_if_fail(item != nullptr, NULL);
1611 g_return_val_if_fail(gr != nullptr, NULL);
1612 g_return_val_if_fail(gr->state == SP_GRADIENT_STATE_VECTOR, NULL);
1613
1614 SPStyle *style = item->style;
1615 g_assert(style != nullptr);
1616
1617 SPPaintServer *ps = nullptr;
1618 if ((fill_or_stroke == Inkscape::FOR_FILL) ? style->fill.isPaintserver() : style->stroke.isPaintserver()) {
1619 ps = (fill_or_stroke == Inkscape::FOR_FILL) ? SP_STYLE_FILL_SERVER(style) : SP_STYLE_STROKE_SERVER(style);
1620 }
1621
1622 if (ps
1623 && ( (type == SP_GRADIENT_TYPE_LINEAR && is<SPLinearGradient>(ps)) ||
1624 (type == SP_GRADIENT_TYPE_RADIAL && is<SPRadialGradient>(ps)) ) )
1625 {
1626
1627 /* Current fill style is the gradient of the required type */
1628 auto current = cast<SPGradient>(ps);
1629
1630 //g_message("hrefcount %d count %d\n", current->hrefcount, count_gradient_hrefs(item, current));
1631
1632 if (!current->isSwatch()
1633 && (current->hrefcount == 1 ||
1634 current->hrefcount == count_gradient_hrefs(item, current))) {
1635
1636 // current is private and it's either used once, or all its uses are by children of item;
1637 // so just change its href to vector
1638
1639 if ( current != gr && current->getVector() != gr ) {
1640 // href is not the vector
1641 sp_gradient_repr_set_link(current->getRepr(), gr);
1642 }
1643 item->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG);
1644 return current;
1645
1646 } else {
1647
1648 // the gradient is not private, or it is shared with someone else;
1649 // normalize it (this includes creating new private if necessary)
1651
1652 g_return_val_if_fail(normalized != nullptr, NULL);
1653
1654 if (normalized != current) {
1655
1656 /* We have to change object style here; recursive because this is used from
1657 * fill&stroke and must work for groups etc. */
1658 sp_style_set_property_url(item, (fill_or_stroke == Inkscape::FOR_FILL) ? "fill" : "stroke", normalized, true);
1659 }
1660 item->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG);
1661 return normalized;
1662 }
1663
1664 } else {
1665 /* Current fill style is not a gradient or wrong type, so construct everything */
1666 /* This is where mesh gradients are constructed. */
1667 g_assert(gr); // TEMP
1669 constructed = sp_gradient_reset_to_userspace(constructed, item);
1670 sp_style_set_property_url(item, ( (fill_or_stroke == Inkscape::FOR_FILL) ? "fill" : "stroke" ), constructed, true);
1671 item->requestDisplayUpdate(( SP_OBJECT_MODIFIED_FLAG |
1672 SP_OBJECT_STYLE_MODIFIED_FLAG ));
1673 return constructed;
1674 }
1675}
1676
1678{
1679#ifdef SP_GR_VERBOSE
1680 g_message("sp_gradient_repr_set_link(%p, %p)", repr, link);
1681#endif
1682 g_return_if_fail(repr != nullptr);
1683
1684 if (link) {
1685 Glib::ustring ref("#");
1686 ref += link->getId();
1688 } else {
1689 repr->removeAttribute("xlink:href");
1690 repr->removeAttribute("href");
1691 }
1692}
1693
1694
1695static void addStop(Inkscape::XML::Node *parent, Color const &color, double opacity, gchar const *offset)
1696{
1697#ifdef SP_GR_VERBOSE
1698 g_message("addStop(%p, %s, %f, %s)", parent, color.toString().c_str(), opacity, offset);
1699#endif
1700 auto doc = parent->document();
1701 Inkscape::XML::Node *repr = doc->createElement("svg:stop");
1702
1703 Color copy = color;
1704 copy.addOpacity(opacity);
1705 SPStop::setColorRepr(repr, copy);
1706 repr->setAttribute( "offset", offset );
1707 parent->appendChild(repr);
1709}
1710
1711/*
1712 * Get default normalized gradient vector of document, create if there is none
1713 */
1714SPGradient *sp_document_default_gradient_vector( SPDocument *document, Color const &color, double opacity, bool singleStop )
1715{
1716 SPDefs *defs = document->getDefs();
1717
1718 Inkscape::XML::Node *repr = document->getReprDoc()->createElement("svg:linearGradient");
1719 defs->getRepr()->addChild(repr, nullptr);
1720
1721 if ( !singleStop ) {
1722 // make auto collection optional
1724 if (prefs->getBool("/option/gradient/auto_collect", true)) {
1725 repr->setAttribute("inkscape:collect", "always");
1726 } else {
1727 repr->setAttribute("inkscape:collect", "never");
1728 }
1729
1730 // set here, but removed when it's edited in the gradient editor
1731 // to further reduce clutter, we could
1732 // (1) here, search gradients by color and return what is found without duplication
1733 // (2) in fill & stroke, show only one copy of each gradient in list
1734 } else {
1735 // Use a swatch prefix for the id, for better UX
1736 repr->setAttribute("id", document->generate_unique_id("swatch"));
1737 }
1738
1739 addStop( repr, color, opacity, "0" );
1740 if ( !singleStop ) {
1741 addStop( repr, color, 0, "1" );
1742 }
1743
1745
1746 /* fixme: This does not look like nice */
1747 SPGradient *gr = static_cast<SPGradient *>(document->getObjectByRepr(repr));
1748 g_assert(gr != nullptr);
1749 /* fixme: Maybe add extra sanity check here */
1751
1752 return gr;
1753}
1754
1756 SPObject *const o, Inkscape::PaintTarget const fill_or_stroke, bool singleStop )
1757{
1758 Color color(0x000000ff);
1759 double opacity = 1.0;
1760 bool for_fill = fill_or_stroke == Inkscape::FOR_FILL;
1761
1762 // if not o or o doesn't use flat color, then take current color of the desktop.
1763 if (auto tmp = sp_desktop_get_color(desktop, for_fill))
1764 color = *tmp;
1765
1766 if (o && o->style) {
1767 // take the color of the object
1768 SPStyle const &style = *(o->style);
1769 SPIPaint const &paint = *style.getFillOrStroke(for_fill);
1770 if (paint.isPaintserver()) {
1771 SPObject *server = for_fill ? o->style->getFillPaintServer() : o->style->getStrokePaintServer();
1772 if ( is<SPLinearGradient>(server) || is<SPRadialGradient>(server) ) {
1773 return cast<SPGradient>(server)->getVector(true);
1774 }
1775 } else if (paint.isColor()) {
1776 color = paint.getColor();
1777 opacity = for_fill ? style.fill_opacity : style.stroke_opacity;
1778 }
1779 }
1780
1781 return sp_document_default_gradient_vector(doc, color, opacity, singleStop);
1782}
1783
1785{
1787
1788 auto list= selection->items();
1789 for (auto i = list.begin(); i != list.end(); ++i) {
1790 sp_item_gradient_invert_vector_color(*i, fill_or_stroke);
1791 }
1792
1793 // we did an undoable action
1794 DocumentUndo::done(desktop->getDocument(), _("Invert gradient colors"), INKSCAPE_ICON("color-gradient"));
1795}
1796
1798{
1801
1802 if (!ev) {
1803 return;
1804 }
1805
1806 GrDrag *drag = ev->get_drag();
1807
1808 // First try selected dragger
1809 if (drag && !drag->selected.empty()) {
1811 } else { // If no drag or no dragger selected, act on selection (both fill and stroke gradients)
1812 auto list= selection->items();
1813 for (auto i = list.begin(); i != list.end(); ++i) {
1816 }
1817 }
1818
1819 // we did an undoable action
1820 DocumentUndo::done(desktop->getDocument(), _("Reverse gradient"), INKSCAPE_ICON("color-gradient"));
1821}
1822
1823void sp_gradient_unset_swatch(SPDesktop *desktop, std::string const &id)
1824{
1825 SPDocument *doc = desktop ? desktop->doc() : nullptr;
1826
1827 if (doc) {
1828 const std::vector<SPObject *> gradients = doc->getResourceList("gradient");
1829 for (auto gradient : gradients) {
1830 auto grad = cast<SPGradient>(gradient);
1831 if ( id == grad->getId() ) {
1832 grad->setSwatch(false);
1833 DocumentUndo::done(doc, _("Delete swatch"), INKSCAPE_ICON("color-gradient"));
1834 break;
1835 }
1836 }
1837 }
1838}
1839
1840/*
1841 * Return a SPItem's gradient
1842 */
1844{
1845 SPIPaint *item_paint = item->style->getFillOrStroke(fillorstroke);
1846 if (item_paint->isPaintserver()) {
1847
1848 SPPaintServer *item_server = fillorstroke ? item->style->getFillPaintServer() : item->style->getStrokePaintServer();
1849
1850 if (is<SPLinearGradient>(item_server) || is<SPRadialGradient>(item_server) ||
1851 (is<SPGradient>(item_server) && cast<SPGradient>(item_server)->getVector()->isSwatch())) {
1852
1853 return cast<SPGradient>(item_server)->getVector();
1854 }
1855 }
1856
1857 return nullptr;
1858}
1859
1860static void get_all_doc_items(std::vector<SPItem*> &list, SPObject *from)
1861{
1862 for (auto& child: from->children) {
1863 if (is<SPItem>(&child)) {
1864 list.push_back(cast<SPItem>(&child));
1865 }
1866 get_all_doc_items(list, &child);
1867 }
1868}
1869
1870std::vector<SPItem*> sp_get_all_document_items(SPDocument* document) {
1871 std::vector<SPItem*> items;
1872 if (document) {
1873 get_all_doc_items(items, document->getRoot());
1874 }
1875 return items;
1876}
1877
1879 if (!document || !gradient) return 0;
1880
1881 int count = 0;
1882 for (auto item : sp_get_all_document_items(document)) {
1883 if (!item->getId()) {
1884 continue;
1885 }
1886 SPGradient* fill = sp_item_get_gradient(item, true); // fill
1887 if (fill == gradient) {
1888 ++count;
1889 }
1890 SPGradient* stroke = sp_item_get_gradient(item, false); // stroke
1891 if (stroke == gradient) {
1892 ++count;
1893 }
1894 }
1895
1896 return count;
1897}
1898
1899/*
1900 Local Variables:
1901 mode:c++
1902 c-file-style:"stroustrup"
1903 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1904 indent-tabs-mode:nil
1905 fill-column:99
1906 End:
1907*/
1908// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
double scale
Definition aa.cpp:228
uint32_t Color
Bezier curve.
3x3 matrix representing an affine transformation.
Definition affine.h:70
Coord descrim() const
Calculate the descriminant.
Definition affine.cpp:434
Affine inverse() const
Compute the inverse matrix.
Definition affine.cpp:388
bool isDegenerate() const override
Check whether the curve has exactly zero length.
Coord nearestTime(Point const &p, Coord from=0, Coord to=1) const override
Compute a time value at which the curve comes closest to a specified point.
Point pointAt(Coord t) const override
Evaluate the curve at a specified time value.
Infinite line on a plane.
Definition line.h:53
Ray ray(Coord t)
Create a ray starting at the specified time value.
Definition line.h:295
Coord angle() const
Angle the line makes with the X axis, in mathematical convention.
Definition line.h:137
Axis-aligned rectangle that can be empty.
Definition rect.h:203
Two-dimensional point that doubles as a vector.
Definition point.h:66
Rotation around the origin.
Definition transforms.h:187
Scaling from the origin.
Definition transforms.h:150
Translation by a vector.
Definition transforms.h:115
This is the root class of the gradient dragging machinery.
std::set< GrDragger * > selected
void selected_reverse_vector()
Color averaged(Color const &other, double pos=0.5) const
Return the average between this and another color.
Definition color.cpp:556
static void done(SPDocument *document, Glib::ustring const &event_description, Glib::ustring const &undo_icon, unsigned int object_modified_tag=0)
static void maybeDone(SPDocument *document, const gchar *keyconst, Glib::ustring const &event_description, Glib::ustring const &undo_icon, unsigned int object_modified_tag=0)
SPItemRange items()
Returns a range of selected SPItems.
Definition object-set.h:255
Preference storage class.
Definition preferences.h:66
bool getBool(Glib::ustring const &pref_path, bool def=false)
Retrieve a Boolean value.
double getDouble(Glib::ustring const &pref_path, double def=0.0, Glib::ustring const &unit="")
Retrieve a floating point value.
static Preferences * get()
Access the singleton Preferences object.
The set of selected SPObjects for a given document and layer model.
Definition selection.h:80
Base class for Event processors.
Definition tool-base.h:107
Interface for refcounted XML nodes.
Definition node.h:80
virtual void addChild(Node *child, Node *after)=0
Insert another node as a child of this node.
virtual void appendChild(Node *child)=0
Append a node as the last child of this node.
void setAttribute(Util::const_char_ptr key, Util::const_char_ptr value)
Change an attribute of this node.
Definition node.cpp:25
virtual Node * duplicate(Document *doc) const =0
Create a duplicate of this node.
virtual Node * firstChild()=0
Get the first child of this node.
bool setAttributeCssDouble(Util::const_char_ptr key, double val)
Set a property attribute to val [slightly rounded], in the format required for CSS properties: in par...
Definition node.cpp:102
virtual char const * attribute(char const *key) const =0
Get the string representation of a node's attribute.
void removeAttribute(Inkscape::Util::const_char_ptr key)
Remove an attribute of this node.
Definition node.h:280
virtual Document * document()=0
Get the node's associated document.
virtual void removeChild(Node *child)=0
Remove a child of this node.
bool setAttributeSvgDouble(Util::const_char_ptr key, double val)
For attributes where an exponent is allowed.
Definition node.cpp:111
To do: update description of desktop.
Definition desktop.h:149
SPDocument * getDocument() const
Definition desktop.h:189
Inkscape::Selection * getSelection() const
Definition desktop.h:188
Inkscape::UI::Tools::ToolBase * getTool() const
Definition desktop.h:187
SPDocument * doc() const
Definition desktop.h:159
Typed SVG document implementation.
Definition document.h:103
SPRoot * getRoot()
Returns our SPRoot.
Definition document.h:202
std::string generate_unique_id(char const *prefix)
Generate a document-wide unique id.
std::vector< SPObject * > const getResourceList(char const *key)
SPDefs * getDefs()
Return the main defs object for the document.
Definition document.cpp:245
Inkscape::XML::Document * getReprDoc()
Our Inkscape::XML::Document.
Definition document.h:213
int ensureUpToDate(unsigned int object_modified_tag=0)
Repeatedly works on getting the document updated, since sometimes it takes more than one pass to get ...
SPObject * getObjectByRepr(Inkscape::XML::Node *repr) const
SPGradient * getObject() const
Gradient.
Definition sp-gradient.h:86
SPGradient * getArray(bool force_private=false)
Returns private mesh of given gradient (the gradient at the end of the href chain which has patches),...
unsigned int state
State in Inkscape gradient system.
SPStop * getFirstStop()
SPGradientVector vector
Linear and Radial Gradients.
Geom::Affine gradientTransform
gradientTransform attribute
Definition sp-gradient.h:99
SPGradientSpread fetchSpread() const
Returns the effective spread of given gradient (climbing up the refs chain if needed).
bool hasStops() const
SPGradientReference * ref
Reference (href)
unsigned int gradientTransform_set
SPGradient * getVector(bool force_private=false)
Returns private vector of given gradient (the gradient at the end of the href chain which has stops),...
SPGradientUnits getUnits() const
bool isSolid() const
int getStopCount() const
bool hasPatches() const
void repr_write_vector()
Writes the gradient's internal vector (whether from its own stops, or inherited from refs) into the g...
void ensureVector()
Forces vector to be built, if not present (i.e.
Paint type internal to SPStyle.
bool isPaintserver() const
bool isColor() const
Colors::Color const & getColor() const
Base class for visual SVG elements.
Definition sp-item.h:109
Geom::Affine i2dt_affine() const
Returns the transformation from item to desktop coords.
Definition sp-item.cpp:1821
Geom::OptRect visualBounds(Geom::Affine const &transform=Geom::identity(), bool wfilter=true, bool wclip=true, bool wmask=true) const
Get item's visual bounding box in this item's coordinate system.
Definition sp-item.cpp:925
std::optional< Inkscape::Colors::Color > color
SPObject is an abstract base class of all of the document nodes at the SVG document level.
Definition sp-object.h:160
void setAttributeOrRemoveIfEmpty(Inkscape::Util::const_char_ptr key, Inkscape::Util::const_char_ptr value)
Definition sp-object.h:785
void requestModified(unsigned int flags)
Requests that a modification notification signal be emitted later (e.g.
SPDocument * document
Definition sp-object.h:188
char const * getId() const
Returns the objects current ID string.
SPStyle * style
Represents the style properties, whether from presentation attributes, the style attribute,...
Definition sp-object.h:248
SPObject * parent
Definition sp-object.h:189
Inkscape::XML::Node * updateRepr(unsigned int flags=SP_OBJECT_WRITE_EXT)
Updates the object's repr based on the object's state.
Inkscape::XML::Node * getRepr()
Returns the XML representation of tree.
SPObject * appendChildRepr(Inkscape::XML::Node *repr)
Append repr as child of this object.
ChildrenList children
Definition sp-object.h:907
unsigned int hrefcount
Definition sp-object.h:186
void requestDisplayUpdate(unsigned int flags)
Queues an deferred update of this object's display.
bool isSwatch() const
Gradient stop.
Definition sp-stop.h:31
float offset
Definition sp-stop.h:38
SPStop * getNextStop()
Virtual write: write object attributes to repr.
Definition sp-stop.cpp:98
static void setColorRepr(Inkscape::XML::Node *node, Inkscape::Colors::Color const &color)
Set the color and opacity directly into the given xml repr.
Definition sp-stop.cpp:150
void setColor(Inkscape::Colors::Color const &color)
Sets the stop color and stop opacity in the style attribute.
Definition sp-stop.cpp:142
Inkscape::Colors::Color getColor() const
Definition sp-stop.cpp:130
SPStop * getPrevStop()
Definition sp-stop.cpp:110
An SVG style object.
Definition style.h:45
SPPaintServer * getFillPaintServer()
Definition style.h:339
T< SPAttr::FILL, SPIPaint > fill
fill
Definition style.h:240
T< SPAttr::STROKE, SPIPaint > stroke
stroke
Definition style.h:247
SPPaintServer * getStrokePaintServer()
Definition style.h:343
T< SPAttr::FILL_OPACITY, SPIScale24 > fill_opacity
fill-opacity
Definition style.h:242
SPIPaint * getFillOrStroke(bool fill_)
Get either the fill or the stroke property.
Definition style.h:355
T< SPAttr::STROKE_OPACITY, SPIScale24 > stroke_opacity
stroke-opacity
Definition style.h:261
Structure representing the intersection of two curves.
TODO: insert short description here.
double c[8][4]
std::optional< Color > sp_desktop_get_color(SPDesktop *desktop, bool is_fill)
Return the desktop's current color.
Editable view implementation.
static char const *const current
Definition dir-util.cpp:71
static char const *const parent
Definition dir-util.cpp:70
TODO: insert short description here.
void sp_gradient_transform_multiply(SPGradient *gradient, Geom::Affine postmul, bool set)
SPStop * sp_get_stop_i(SPGradient *gradient, guint stop_i)
static void sp_gradient_repr_set_link(Inkscape::XML::Node *repr, SPGradient *gr)
void sp_gradient_invert_selected_gradients(SPDesktop *desktop, Inkscape::PaintTarget fill_or_stroke)
Color sp_item_gradient_stop_query_style(SPItem *item, GrPointType point_type, guint point_i, Inkscape::PaintTarget fill_or_stroke)
void sp_gradient_unset_swatch(SPDesktop *desktop, std::string const &id)
Geom::Point getGradientCoords(SPItem *item, GrPointType point_type, guint point_i, Inkscape::PaintTarget fill_or_stroke)
Returns the position of point point_type of the gradient applied to item (either fill_or_stroke),...
void sp_item_gradient_stop_set_style(SPItem *item, GrPointType point_type, guint point_i, Inkscape::PaintTarget fill_or_stroke, SPCSSAttr *stop)
SPStop * sp_item_gradient_get_stop(SPItem *item, GrPointType point_type, guint point_i, Inkscape::PaintTarget fill_or_stroke)
void sp_gradient_reverse_selected_gradients(SPDesktop *desktop)
SPGradient * sp_gradient_fork_vector_if_necessary(SPGradient *gr)
int sp_get_gradient_refcount(SPDocument *document, SPGradient *gradient)
double midpoint_offset_hack(double offset)
SPGradient * sp_item_gradient_get_vector(SPItem *item, Inkscape::PaintTarget fill_or_stroke)
static SPGradient * sp_gradient_fork_private_if_necessary(SPGradient *gr, SPGradient *shared, SPGradientType type, SPObject *o)
If gr has other users, create a new shared; also check if gr links to shared, relink if not.
std::vector< SPItem * > sp_get_all_document_items(SPDocument *document)
std::pair< SPStop *, SPStop * > sp_get_before_after_stops(SPStop *stop)
SPStop * sp_get_nth_stop(SPGradient *gradient, guint index)
void sp_set_gradient_stop_color(SPDocument *document, SPStop *stop, Color const &color)
void sp_gradient_reverse_vector(SPGradient *gradient)
static std::pair< SPStop *, SPStop * > get_before_after_stops(SPGradient *gradient, double offset)
SPStop * sp_last_stop(SPGradient *gradient)
static SPGradient * sp_gradient_get_private_normalized(SPDocument *document, SPGradient *shared, SPGradientType type)
Creates new private gradient for the given shared gradient.
SPStop * sp_gradient_add_stop_at(SPGradient *gradient, double offset)
void sp_item_gradient_set_coords(SPItem *item, GrPointType point_type, guint point_i, Geom::Point p_w, Inkscape::PaintTarget fill_or_stroke, bool write_repr, bool scale)
Set the position of point point_type of the gradient applied to item (either fill_or_stroke) to p_w (...
SPStop * sp_vector_add_stop(SPGradient *vector, SPStop *prev_stop, SPStop *next_stop, gfloat offset)
SPStop * sp_gradient_add_stop(SPGradient *gradient, SPStop *current)
void sp_item_gradient_invert_vector_color(SPItem *item, Inkscape::PaintTarget fill_or_stroke)
SPGradient * sp_gradient_ensure_vector_normalized(SPGradient *gr)
Either normalizes given gradient to vector, or returns fresh normalized vector - in latter case,...
SPGradientSpread sp_item_gradient_get_spread(SPItem *item, Inkscape::PaintTarget fill_or_stroke)
void sp_gradient_delete_stop(SPGradient *gradient, SPStop *stop)
SPGradient * sp_gradient_get_forked_vector_if_necessary(SPGradient *gradient, bool force_vector)
Obtain the vector from the gradient.
SPGradient * getGradient(SPItem *item, Inkscape::PaintTarget fill_or_stroke)
Fetches either the fill or the stroke gradient from the given item.
SPGradient * sp_document_default_gradient_vector(SPDocument *document, Color const &color, double opacity, bool singleStop)
SPGradient * sp_gradient_vector_for_object(SPDocument *const doc, SPDesktop *const desktop, SPObject *const o, Inkscape::PaintTarget const fill_or_stroke, bool singleStop)
Return the preferred vector for o, made from (in order of preference) its current vector,...
static void addStop(Inkscape::XML::Node *parent, Color const &color, double opacity, gchar const *offset)
static guint count_gradient_hrefs(SPObject *o, SPGradient *gr)
Count how many times gr is used by the styles of o and its descendants.
static bool verify_grad(SPGradient *gradient)
static void get_all_doc_items(std::vector< SPItem * > &list, SPObject *from)
SPGradient * sp_gradient_convert_to_userspace(SPGradient *gr, SPItem *item, gchar const *property)
Convert an item's gradient to userspace if necessary, also fork it if necessary.
guint sp_number_of_stops_before_stop(SPGradient *gradient, SPStop *target)
void sp_item_gradient_reverse_vector(SPItem *item, Inkscape::PaintTarget fill_or_stroke)
SPGradient * sp_item_set_gradient(SPItem *item, SPGradient *gr, SPGradientType type, Inkscape::PaintTarget fill_or_stroke)
Sets item fill or stroke to the gradient of the specified type with given vector, creating new privat...
SPGradient * sp_gradient_reset_to_userspace(SPGradient *gr, SPItem *item)
Convert an item's gradient to userspace without preserving coords, setting them to defaults instead.
void sp_repr_set_css_double(Inkscape::XML::Node *node, const char *key, double value)
SPGradient * sp_item_get_gradient(SPItem *item, bool fillorstroke)
BezierCurveN< 1 > LineSegment
Line segment.
double Coord
Floating point type used to store coordinates.
Definition coord.h:76
@ Y
Definition coord.h:48
@ X
Definition coord.h:48
Macro for icon names used in Inkscape.
SPItem * item
Inkscape::XML::Node * node
Infinite straight line.
double offset
Geom::Point start
Geom::Point end
double atan2(Point const &p)
Affine identity()
Create an identity matrix.
Definition affine.h:210
SBasis L2(D2< SBasis > const &a, unsigned k)
Definition d2-sbasis.cpp:42
static R & release(R &r)
Decrements the reference count of a anchored object.
Helper class to stream background task notifications as a series of messages.
void setHrefAttribute(XML::Node &node, Util::const_char_ptr value)
If the 'href' attribute already exists for the given node, then set a new value for it.
std::vector< PaintTarget > const & allPaintTargets()
Convenience function to access a common vector of all enum values.
static cairo_user_data_key_t key
Ocnode * child[8]
Definition quantize.cpp:33
Ocnode ** ref
Definition quantize.cpp:32
void sp_repr_css_change(Node *repr, SPCSSAttr *css, gchar const *attr)
Creates a new SPCSAttr with the values filled from a repr, merges in properties from the given SPCSAt...
Definition repr-css.cpp:358
double sp_repr_css_double_property(SPCSSAttr *css, gchar const *name, double defval)
Return the value of a style property if property define, or a default value if not.
Definition repr-css.cpp:213
char const * sp_repr_css_property(SPCSSAttr *css, gchar const *name, gchar const *defval)
Returns a character string of the value of a given style property or a default value if the attribute...
Definition repr-css.cpp:147
GList * items
TODO: insert short description here.
SPGradientSpread
@ SP_GRADIENT_SPREAD_PAD
@ SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX
@ SP_GRADIENT_STATE_VECTOR
Definition sp-gradient.h:42
@ SP_GRADIENT_STATE_UNKNOWN
Definition sp-gradient.h:41
GrPointType
Definition sp-gradient.h:46
@ POINT_LG_END
Definition sp-gradient.h:48
@ POINT_RG_R2
Definition sp-gradient.h:52
@ POINT_RG_MID2
Definition sp-gradient.h:55
@ POINT_MG_CORNER
Definition sp-gradient.h:56
@ POINT_RG_CENTER
Definition sp-gradient.h:50
@ POINT_MG_TENSOR
Definition sp-gradient.h:58
@ POINT_LG_BEGIN
Definition sp-gradient.h:47
@ POINT_RG_MID1
Definition sp-gradient.h:54
@ POINT_RG_R1
Definition sp-gradient.h:51
@ POINT_MG_HANDLE
Definition sp-gradient.h:57
@ POINT_LG_MID
Definition sp-gradient.h:49
@ POINT_RG_FOCUS
Definition sp-gradient.h:53
SPGradientType
Definition sp-gradient.h:33
@ SP_GRADIENT_TYPE_LINEAR
Definition sp-gradient.h:35
@ SP_GRADIENT_TYPE_MESH
Definition sp-gradient.h:37
@ SP_GRADIENT_TYPE_RADIAL
Definition sp-gradient.h:36
TODO: insert short description here.
TODO: insert short description here.
TODO: insert short description here.
SPRoot: SVG <svg> implementation.
TODO: insert short description here.
TODO: insert short description here.
Interface for XML documents.
Definition document.h:43
virtual Node * createElement(char const *name)=0
void sp_style_set_property_url(SPObject *item, gchar const *property, SPObject *linked, bool recursive)
Definition style.cpp:1380
SPStyle - a style object for SPItem objects.
std::string sp_svg_transform_write(Geom::Affine const &transform)
int index
SPDesktop * desktop
double height
double width
Affine transformation classes.