1 #define __SP_GRADIENT_C__
3 /** \file
4 * SPGradient, SPStop, SPLinearGradient, SPRadialGradient.
5 */
6 /*
7 * Authors:
8 * Lauris Kaplinski <lauris@kaplinski.com>
9 * bulia byak <buliabyak@users.sf.net>
10 *
11 * Copyright (C) 1999-2002 Lauris Kaplinski
12 * Copyright (C) 2000-2001 Ximian, Inc.
13 * Copyright (C) 2004 David Turner
14 *
15 * Released under GNU GPL, read the file 'COPYING' for more information
16 *
17 */
19 #define noSP_GRADIENT_VERBOSE
22 #include <libnr/nr-matrix-div.h>
23 #include <libnr/nr-matrix-fns.h>
24 #include <libnr/nr-matrix-ops.h>
25 #include <libnr/nr-matrix-scale-ops.h>
26 #include <libnr/nr-matrix-translate-ops.h>
27 #include "libnr/nr-scale-translate-ops.h"
30 #include "display/nr-gradient-gpl.h"
31 #include "svg/svg.h"
32 #include "svg/css-ostringstream.h"
33 #include "attributes.h"
34 #include "document-private.h"
35 #include "gradient-chemistry.h"
36 #include "sp-gradient-reference.h"
37 #include "sp-linear-gradient.h"
38 #include "sp-radial-gradient.h"
39 #include "sp-stop.h"
40 #include "streq.h"
41 #include "uri.h"
42 #include "xml/repr.h"
44 #define SP_MACROS_SILENT
45 #include "macros.h"
47 /// Has to be power of 2
48 #define NCOLORS NR_GRADIENT_VECTOR_LENGTH
50 static void sp_stop_class_init(SPStopClass *klass);
51 static void sp_stop_init(SPStop *stop);
53 static void sp_stop_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
54 static void sp_stop_set(SPObject *object, unsigned key, gchar const *value);
55 static Inkscape::XML::Node *sp_stop_write(SPObject *object, Inkscape::XML::Node *repr, guint flags);
57 static SPObjectClass *stop_parent_class;
59 /**
60 * Registers SPStop class and returns its type.
61 */
62 GType
63 sp_stop_get_type()
64 {
65 static GType type = 0;
66 if (!type) {
67 GTypeInfo info = {
68 sizeof(SPStopClass),
69 NULL, NULL,
70 (GClassInitFunc) sp_stop_class_init,
71 NULL, NULL,
72 sizeof(SPStop),
73 16,
74 (GInstanceInitFunc) sp_stop_init,
75 NULL, /* value_table */
76 };
77 type = g_type_register_static(SP_TYPE_OBJECT, "SPStop", &info, (GTypeFlags)0);
78 }
79 return type;
80 }
82 /**
83 * Callback to initialize SPStop vtable.
84 */
85 static void sp_stop_class_init(SPStopClass *klass)
86 {
87 SPObjectClass *sp_object_class = (SPObjectClass *) klass;
89 stop_parent_class = (SPObjectClass *) g_type_class_ref(SP_TYPE_OBJECT);
91 sp_object_class->build = sp_stop_build;
92 sp_object_class->set = sp_stop_set;
93 sp_object_class->write = sp_stop_write;
94 }
96 /**
97 * Callback to initialize SPStop object.
98 */
99 static void
100 sp_stop_init(SPStop *stop)
101 {
102 stop->offset = 0.0;
103 stop->currentColor = false;
104 sp_color_set_rgb_rgba32(&stop->specified_color, 0x000000ff);
105 stop->opacity = 1.0;
106 }
108 /**
109 * Virtual build: set stop attributes from its associated XML node.
110 */
111 static void sp_stop_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
112 {
113 if (((SPObjectClass *) stop_parent_class)->build)
114 (* ((SPObjectClass *) stop_parent_class)->build)(object, document, repr);
116 sp_object_read_attr(object, "offset");
117 sp_object_read_attr(object, "stop-color");
118 sp_object_read_attr(object, "stop-opacity");
119 sp_object_read_attr(object, "style");
120 }
122 /**
123 * Virtual set: set attribute to value.
124 */
125 static void
126 sp_stop_set(SPObject *object, unsigned key, gchar const *value)
127 {
128 SPStop *stop = SP_STOP(object);
130 switch (key) {
131 case SP_ATTR_STYLE: {
132 /** \todo
133 * fixme: We are reading simple values 3 times during build (Lauris).
134 * \par
135 * We need presentation attributes etc.
136 * \par
137 * remove the hackish "style reading" from here: see comments in
138 * sp_object_get_style_property about the bugs in our current
139 * approach. However, note that SPStyle doesn't currently have
140 * stop-color and stop-opacity properties.
141 */
142 {
143 gchar const *p = sp_object_get_style_property(object, "stop-color", "black");
144 if (streq(p, "currentColor")) {
145 stop->currentColor = true;
146 } else {
147 guint32 const color = sp_svg_read_color(p, 0);
148 sp_color_set_rgb_rgba32(&stop->specified_color, color);
149 }
150 }
151 {
152 gchar const *p = sp_object_get_style_property(object, "stop-opacity", "1");
153 gdouble opacity = sp_svg_read_percentage(p, stop->opacity);
154 stop->opacity = opacity;
155 }
156 object->requestModified(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG);
157 break;
158 }
159 case SP_PROP_STOP_COLOR: {
160 {
161 gchar const *p = sp_object_get_style_property(object, "stop-color", "black");
162 if (streq(p, "currentColor")) {
163 stop->currentColor = true;
164 } else {
165 stop->currentColor = false;
166 guint32 const color = sp_svg_read_color(p, 0);
167 sp_color_set_rgb_rgba32(&stop->specified_color, color);
168 }
169 }
170 object->requestModified(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG);
171 break;
172 }
173 case SP_PROP_STOP_OPACITY: {
174 {
175 gchar const *p = sp_object_get_style_property(object, "stop-opacity", "1");
176 gdouble opacity = sp_svg_read_percentage(p, stop->opacity);
177 stop->opacity = opacity;
178 }
179 object->requestModified(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG);
180 break;
181 }
182 case SP_ATTR_OFFSET: {
183 stop->offset = sp_svg_read_percentage(value, 0.0);
184 object->requestModified(SP_OBJECT_MODIFIED_FLAG);
185 break;
186 }
187 default: {
188 if (((SPObjectClass *) stop_parent_class)->set)
189 (* ((SPObjectClass *) stop_parent_class)->set)(object, key, value);
190 break;
191 }
192 }
193 }
195 /**
196 * Virtual write: write object attributes to repr.
197 */
198 static Inkscape::XML::Node *
199 sp_stop_write(SPObject *object, Inkscape::XML::Node *repr, guint flags)
200 {
201 SPStop *stop = SP_STOP(object);
203 if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
204 repr = sp_repr_new("svg:stop");
205 }
208 Inkscape::CSSOStringStream os;
209 os << "stop-color:";
210 if (stop->currentColor) {
211 os << "currentColor";
212 } else {
213 gchar c[64];
214 sp_svg_write_color(c, 64, sp_color_get_rgba32_ualpha(&stop->specified_color, 255));
215 os << c;
216 }
217 os << ";stop-opacity:" << stop->opacity;
218 repr->setAttribute("style", os.str().c_str());
219 repr->setAttribute("stop-color", NULL);
220 repr->setAttribute("stop-opacity", NULL);
221 sp_repr_set_css_double(repr, "offset", stop->offset);
222 /* strictly speaking, offset an SVG <number> rather than a CSS one, but exponents make no sense
223 * for offset proportions. */
225 if (((SPObjectClass *) stop_parent_class)->write)
226 (* ((SPObjectClass *) stop_parent_class)->write)(object, repr, flags);
228 return repr;
229 }
231 /**
232 * Return stop's color as 32bit value.
233 */
234 guint32
235 sp_stop_get_rgba32(SPStop const *const stop)
236 {
237 guint32 rgb0 = 0;
238 /* Default value: arbitrarily black. (SVG1.1 and CSS2 both say that the initial
239 * value depends on user agent, and don't give any further restrictions that I can
240 * see.) */
241 if (stop->currentColor) {
242 char const *str = sp_object_get_style_property(stop, "color", NULL);
243 if (str) {
244 rgb0 = sp_svg_read_color(str, rgb0);
245 }
246 unsigned const alpha = static_cast<unsigned>(stop->opacity * 0xff + 0.5);
247 g_return_val_if_fail((alpha & ~0xff) == 0,
248 rgb0 | 0xff);
249 return rgb0 | alpha;
250 } else {
251 return sp_color_get_rgba32_falpha(&stop->specified_color, stop->opacity);
252 }
253 }
255 /**
256 * Return stop's color as SPColor.
257 */
258 static SPColor
259 sp_stop_get_color(SPStop const *const stop)
260 {
261 if (stop->currentColor) {
262 char const *str = sp_object_get_style_property(stop, "color", NULL);
263 guint32 const dfl = 0;
264 /* Default value: arbitrarily black. (SVG1.1 and CSS2 both say that the initial
265 * value depends on user agent, and don't give any further restrictions that I can
266 * see.) */
267 guint32 color = dfl;
268 if (str) {
269 color = sp_svg_read_color(str, dfl);
270 }
271 SPColor ret;
272 sp_color_set_rgb_rgba32(&ret, color);
273 return ret;
274 } else {
275 return stop->specified_color;
276 }
277 }
279 /*
280 * Gradient
281 */
283 static void sp_gradient_class_init(SPGradientClass *klass);
284 static void sp_gradient_init(SPGradient *gr);
286 static void sp_gradient_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
287 static void sp_gradient_release(SPObject *object);
288 static void sp_gradient_set(SPObject *object, unsigned key, gchar const *value);
289 static void sp_gradient_child_added(SPObject *object,
290 Inkscape::XML::Node *child,
291 Inkscape::XML::Node *ref);
292 static void sp_gradient_remove_child(SPObject *object, Inkscape::XML::Node *child);
293 static void sp_gradient_modified(SPObject *object, guint flags);
294 static Inkscape::XML::Node *sp_gradient_write(SPObject *object, Inkscape::XML::Node *repr,
295 guint flags);
297 static void gradient_ref_modified(SPObject *href, guint flags, SPGradient *gradient);
299 static bool sp_gradient_invalidate_vector(SPGradient *gr);
300 static void sp_gradient_rebuild_vector(SPGradient *gr);
302 static void gradient_ref_changed(SPObject *old_ref, SPObject *ref, SPGradient *gradient);
304 static SPPaintServerClass *gradient_parent_class;
306 /**
307 * Registers SPGradient class and returns its type.
308 */
309 GType
310 sp_gradient_get_type()
311 {
312 static GType gradient_type = 0;
313 if (!gradient_type) {
314 GTypeInfo gradient_info = {
315 sizeof(SPGradientClass),
316 NULL, NULL,
317 (GClassInitFunc) sp_gradient_class_init,
318 NULL, NULL,
319 sizeof(SPGradient),
320 16,
321 (GInstanceInitFunc) sp_gradient_init,
322 NULL, /* value_table */
323 };
324 gradient_type = g_type_register_static(SP_TYPE_PAINT_SERVER, "SPGradient",
325 &gradient_info, (GTypeFlags)0);
326 }
327 return gradient_type;
328 }
330 /**
331 * SPGradient vtable initialization.
332 */
333 static void
334 sp_gradient_class_init(SPGradientClass *klass)
335 {
336 SPObjectClass *sp_object_class = (SPObjectClass *) klass;
338 gradient_parent_class = (SPPaintServerClass *)g_type_class_ref(SP_TYPE_PAINT_SERVER);
340 sp_object_class->build = sp_gradient_build;
341 sp_object_class->release = sp_gradient_release;
342 sp_object_class->set = sp_gradient_set;
343 sp_object_class->child_added = sp_gradient_child_added;
344 sp_object_class->remove_child = sp_gradient_remove_child;
345 sp_object_class->modified = sp_gradient_modified;
346 sp_object_class->write = sp_gradient_write;
347 }
349 /**
350 * Callback for SPGradient object initialization.
351 */
352 static void
353 sp_gradient_init(SPGradient *gr)
354 {
355 gr->ref = new SPGradientReference(SP_OBJECT(gr));
356 gr->ref->changedSignal().connect(sigc::bind(sigc::ptr_fun(gradient_ref_changed), gr));
358 /** \todo
359 * Fixme: reprs being rearranged (e.g. via the XML editor)
360 * may require us to clear the state.
361 */
362 gr->state = SP_GRADIENT_STATE_UNKNOWN;
364 gr->units = SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX;
365 gr->units_set = FALSE;
367 gr->gradientTransform = NR::identity();
368 gr->gradientTransform_set = FALSE;
370 gr->spread = SP_GRADIENT_SPREAD_PAD;
371 gr->spread_set = FALSE;
373 gr->has_stops = FALSE;
375 gr->vector.built = false;
376 gr->vector.stops.clear();
378 gr->color = NULL;
379 }
381 /**
382 * Virtual build: set gradient attributes from its associated repr.
383 */
384 static void
385 sp_gradient_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
386 {
387 SPGradient *gradient = SP_GRADIENT(object);
389 if (((SPObjectClass *) gradient_parent_class)->build)
390 (* ((SPObjectClass *) gradient_parent_class)->build)(object, document, repr);
392 SPObject *ochild;
393 for ( ochild = sp_object_first_child(object) ; ochild ; ochild = SP_OBJECT_NEXT(ochild) ) {
394 if (SP_IS_STOP(ochild)) {
395 gradient->has_stops = TRUE;
396 break;
397 }
398 }
400 sp_object_read_attr(object, "gradientUnits");
401 sp_object_read_attr(object, "gradientTransform");
402 sp_object_read_attr(object, "spreadMethod");
403 sp_object_read_attr(object, "xlink:href");
405 /* Register ourselves */
406 sp_document_add_resource(document, "gradient", object);
407 }
409 /**
410 * Virtual release of SPGradient members before destruction.
411 */
412 static void
413 sp_gradient_release(SPObject *object)
414 {
415 SPGradient *gradient = (SPGradient *) object;
417 #ifdef SP_GRADIENT_VERBOSE
418 g_print("Releasing gradient %s\n", SP_OBJECT_ID(object));
419 #endif
421 if (SP_OBJECT_DOCUMENT(object)) {
422 /* Unregister ourselves */
423 sp_document_remove_resource(SP_OBJECT_DOCUMENT(object), "gradient", SP_OBJECT(object));
424 }
426 if (gradient->ref) {
427 if (gradient->ref->getObject()) {
428 sp_signal_disconnect_by_data(gradient->ref->getObject(), gradient);
429 }
430 gradient->ref->detach();
431 delete gradient->ref;
432 gradient->ref = NULL;
433 }
435 if (gradient->color) {
436 g_free(gradient->color);
437 gradient->color = NULL;
438 }
440 if (((SPObjectClass *) gradient_parent_class)->release)
441 ((SPObjectClass *) gradient_parent_class)->release(object);
442 }
444 /**
445 * Set gradient attribute to value.
446 */
447 static void
448 sp_gradient_set(SPObject *object, unsigned key, gchar const *value)
449 {
450 SPGradient *gr = SP_GRADIENT(object);
452 switch (key) {
453 case SP_ATTR_GRADIENTUNITS:
454 if (value) {
455 if (!strcmp(value, "userSpaceOnUse")) {
456 gr->units = SP_GRADIENT_UNITS_USERSPACEONUSE;
457 } else {
458 gr->units = SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX;
459 }
460 gr->units_set = TRUE;
461 } else {
462 gr->units = SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX;
463 gr->units_set = FALSE;
464 }
465 object->requestModified(SP_OBJECT_MODIFIED_FLAG);
466 break;
467 case SP_ATTR_GRADIENTTRANSFORM: {
468 NR::Matrix t;
469 if (value && sp_svg_transform_read(value, &t)) {
470 gr->gradientTransform = t;
471 gr->gradientTransform_set = TRUE;
472 } else {
473 gr->gradientTransform = NR::identity();
474 gr->gradientTransform_set = FALSE;
475 }
476 object->requestModified(SP_OBJECT_MODIFIED_FLAG);
477 break;
478 }
479 case SP_ATTR_SPREADMETHOD:
480 if (value) {
481 if (!strcmp(value, "reflect")) {
482 gr->spread = SP_GRADIENT_SPREAD_REFLECT;
483 } else if (!strcmp(value, "repeat")) {
484 gr->spread = SP_GRADIENT_SPREAD_REPEAT;
485 } else {
486 gr->spread = SP_GRADIENT_SPREAD_PAD;
487 }
488 gr->spread_set = TRUE;
489 } else {
490 gr->spread_set = FALSE;
491 }
492 object->requestModified(SP_OBJECT_MODIFIED_FLAG);
493 break;
494 case SP_ATTR_XLINK_HREF:
495 if (value) {
496 try {
497 gr->ref->attach(Inkscape::URI(value));
498 } catch (Inkscape::BadURIException &e) {
499 g_warning("%s", e.what());
500 gr->ref->detach();
501 }
502 } else {
503 gr->ref->detach();
504 }
505 break;
506 default:
507 if (((SPObjectClass *) gradient_parent_class)->set)
508 ((SPObjectClass *) gradient_parent_class)->set(object, key, value);
509 break;
510 }
511 }
513 /**
514 * Gets called when the gradient is (re)attached to another gradient.
515 */
516 static void
517 gradient_ref_changed(SPObject *old_ref, SPObject *ref, SPGradient *gr)
518 {
519 if (old_ref) {
520 sp_signal_disconnect_by_data(old_ref, gr);
521 }
522 if ( SP_IS_GRADIENT(ref)
523 && ref != gr )
524 {
525 g_signal_connect(G_OBJECT(ref), "modified", G_CALLBACK(gradient_ref_modified), gr);
526 }
527 /// \todo Fixme: what should the flags (second) argument be? */
528 gradient_ref_modified(ref, 0, gr);
529 }
531 /**
532 * Callback for child_added event.
533 */
534 static void
535 sp_gradient_child_added(SPObject *object, Inkscape::XML::Node *child, Inkscape::XML::Node *ref)
536 {
537 SPGradient *gr = SP_GRADIENT(object);
539 sp_gradient_invalidate_vector(gr);
541 if (((SPObjectClass *) gradient_parent_class)->child_added)
542 (* ((SPObjectClass *) gradient_parent_class)->child_added)(object, child, ref);
544 SPObject *ochild = sp_object_get_child_by_repr(object, child);
545 if ( ochild && SP_IS_STOP(ochild) ) {
546 gr->has_stops = TRUE;
547 }
549 /// \todo Fixme: should we schedule "modified" here?
550 object->requestModified(SP_OBJECT_MODIFIED_FLAG);
551 }
553 /**
554 * Callback for remove_child event.
555 */
556 static void
557 sp_gradient_remove_child(SPObject *object, Inkscape::XML::Node *child)
558 {
559 SPGradient *gr = SP_GRADIENT(object);
561 sp_gradient_invalidate_vector(gr);
563 if (((SPObjectClass *) gradient_parent_class)->remove_child)
564 (* ((SPObjectClass *) gradient_parent_class)->remove_child)(object, child);
566 gr->has_stops = FALSE;
567 SPObject *ochild;
568 for ( ochild = sp_object_first_child(object) ; ochild ; ochild = SP_OBJECT_NEXT(ochild) ) {
569 if (SP_IS_STOP(ochild)) {
570 gr->has_stops = TRUE;
571 break;
572 }
573 }
575 /* Fixme: should we schedule "modified" here? */
576 object->requestModified(SP_OBJECT_MODIFIED_FLAG);
577 }
579 /**
580 * Callback for modified event.
581 */
582 static void
583 sp_gradient_modified(SPObject *object, guint flags)
584 {
585 SPGradient *gr = SP_GRADIENT(object);
587 if (flags & SP_OBJECT_CHILD_MODIFIED_FLAG) {
588 sp_gradient_invalidate_vector(gr);
589 }
591 if (flags & SP_OBJECT_MODIFIED_FLAG) flags |= SP_OBJECT_PARENT_MODIFIED_FLAG;
592 flags &= SP_OBJECT_MODIFIED_CASCADE;
594 // FIXME: climb up the ladder of hrefs
595 GSList *l = NULL;
596 for (SPObject *child = sp_object_first_child(object) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
597 g_object_ref(G_OBJECT(child));
598 l = g_slist_prepend(l, child);
599 }
600 l = g_slist_reverse(l);
601 while (l) {
602 SPObject *child = SP_OBJECT(l->data);
603 l = g_slist_remove(l, child);
604 if (flags || (child->mflags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG))) {
605 child->emitModified(flags);
606 }
607 g_object_unref(G_OBJECT(child));
608 }
609 }
611 /**
612 * Write gradient attributes to repr.
613 */
614 static Inkscape::XML::Node *
615 sp_gradient_write(SPObject *object, Inkscape::XML::Node *repr, guint flags)
616 {
617 SPGradient *gr = SP_GRADIENT(object);
619 if (((SPObjectClass *) gradient_parent_class)->write)
620 (* ((SPObjectClass *) gradient_parent_class)->write)(object, repr, flags);
622 if (flags & SP_OBJECT_WRITE_BUILD) {
623 GSList *l = NULL;
624 for (SPObject *child = sp_object_first_child(object); child; child = SP_OBJECT_NEXT(child)) {
625 Inkscape::XML::Node *crepr;
626 crepr = child->updateRepr(NULL, flags);
627 if (crepr) l = g_slist_prepend(l, crepr);
628 }
629 while (l) {
630 repr->addChild((Inkscape::XML::Node *) l->data, NULL);
631 Inkscape::GC::release((Inkscape::XML::Node *) l->data);
632 l = g_slist_remove(l, l->data);
633 }
634 }
636 if (gr->ref->getURI()) {
637 gchar *uri_string = gr->ref->getURI()->toString();
638 repr->setAttribute("xlink:href", uri_string);
639 g_free(uri_string);
640 }
642 if ((flags & SP_OBJECT_WRITE_ALL) || gr->units_set) {
643 switch (gr->units) {
644 case SP_GRADIENT_UNITS_USERSPACEONUSE:
645 repr->setAttribute("gradientUnits", "userSpaceOnUse");
646 break;
647 default:
648 repr->setAttribute("gradientUnits", "objectBoundingBox");
649 break;
650 }
651 }
653 if ((flags & SP_OBJECT_WRITE_ALL) || gr->gradientTransform_set) {
654 gchar c[256];
655 if (sp_svg_transform_write(c, 256, gr->gradientTransform)) {
656 repr->setAttribute("gradientTransform", c);
657 } else {
658 repr->setAttribute("gradientTransform", NULL);
659 }
660 }
662 if ((flags & SP_OBJECT_WRITE_ALL) || gr->spread_set) {
663 /* FIXME: Ensure that gr->spread is the inherited value
664 * if !gr->spread_set. Not currently happening: see sp_gradient_modified.
665 */
666 switch (gr->spread) {
667 case SP_GRADIENT_SPREAD_REFLECT:
668 repr->setAttribute("spreadMethod", "reflect");
669 break;
670 case SP_GRADIENT_SPREAD_REPEAT:
671 repr->setAttribute("spreadMethod", "repeat");
672 break;
673 default:
674 repr->setAttribute("spreadMethod", "pad");
675 break;
676 }
677 }
679 return repr;
680 }
682 /**
683 * Forces the vector to be built, if not present (i.e., changed).
684 *
685 * \pre SP_IS_GRADIENT(gradient).
686 */
687 void
688 sp_gradient_ensure_vector(SPGradient *gradient)
689 {
690 g_return_if_fail(gradient != NULL);
691 g_return_if_fail(SP_IS_GRADIENT(gradient));
693 if (!gradient->vector.built) {
694 sp_gradient_rebuild_vector(gradient);
695 }
696 }
698 /**
699 * Set units property of gradient and emit modified.
700 */
701 void
702 sp_gradient_set_units(SPGradient *gr, SPGradientUnits units)
703 {
704 if (units != gr->units) {
705 gr->units = units;
706 gr->units_set = TRUE;
707 SP_OBJECT(gr)->requestModified(SP_OBJECT_MODIFIED_FLAG);
708 }
709 }
711 /**
712 * Set spread property of gradient and emit modified.
713 */
714 void
715 sp_gradient_set_spread(SPGradient *gr, SPGradientSpread spread)
716 {
717 if (spread != gr->spread) {
718 gr->spread = spread;
719 gr->spread_set = TRUE;
720 SP_OBJECT(gr)->requestModified(SP_OBJECT_MODIFIED_FLAG);
721 }
722 }
724 /**
725 * Returns the first of {src, src-\>ref-\>getObject(),
726 * src-\>ref-\>getObject()-\>ref-\>getObject(),...}
727 * for which \a match is true, or NULL if none found.
728 *
729 * The raison d'être of this routine is that it correctly handles cycles in the href chain (e.g., if
730 * a gradient gives itself as its href, or if each of two gradients gives the other as its href).
731 *
732 * \pre SP_IS_GRADIENT(src).
733 */
734 static SPGradient *
735 chase_hrefs(SPGradient *const src, bool (*match)(SPGradient const *))
736 {
737 g_return_val_if_fail(SP_IS_GRADIENT(src), NULL);
739 /* Use a pair of pointers for detecting loops: p1 advances half as fast as p2. If there is a
740 loop, then once p1 has entered the loop, we'll detect it the next time the distance between
741 p1 and p2 is a multiple of the loop size. */
742 SPGradient *p1 = src, *p2 = src;
743 bool do1 = false;
744 for (;;) {
745 if (match(p2)) {
746 return p2;
747 }
749 p2 = p2->ref->getObject();
750 if (!p2) {
751 return p2;
752 }
753 if (do1) {
754 p1 = p1->ref->getObject();
755 }
756 do1 = !do1;
758 if ( p2 == p1 ) {
759 /* We've been here before, so return NULL to indicate that no matching gradient found
760 * in the chain. */
761 return NULL;
762 }
763 }
764 }
766 /**
767 * True if gradient has stops.
768 */
769 static bool
770 has_stops(SPGradient const *gr)
771 {
772 return SP_GRADIENT_HAS_STOPS(gr);
773 }
775 /**
776 * True if gradient has spread set.
777 */
778 static bool
779 has_spread_set(SPGradient const *gr)
780 {
781 return gr->spread_set;
782 }
785 /**
786 * Returns private vector of given gradient (the gradient at the end of the href chain which has
787 * stops), optionally normalizing it.
788 *
789 * \pre SP_IS_GRADIENT(gradient).
790 * \pre There exists a gradient in the chain that has stops.
791 */
792 SPGradient *
793 sp_gradient_get_vector(SPGradient *gradient, bool force_vector)
794 {
795 g_return_val_if_fail(gradient != NULL, NULL);
796 g_return_val_if_fail(SP_IS_GRADIENT(gradient), NULL);
798 SPGradient *const src = chase_hrefs(gradient, has_stops);
799 return ( force_vector
800 ? sp_gradient_ensure_vector_normalized(src)
801 : src );
802 }
804 /**
805 * Returns the effective spread of given gradient (climbing up the refs chain if needed).
806 *
807 * \pre SP_IS_GRADIENT(gradient).
808 */
809 SPGradientSpread
810 sp_gradient_get_spread(SPGradient *gradient)
811 {
812 g_return_val_if_fail(SP_IS_GRADIENT(gradient), SP_GRADIENT_SPREAD_PAD);
814 SPGradient const *src = chase_hrefs(gradient, has_spread_set);
815 return ( src
816 ? src->spread
817 : SP_GRADIENT_SPREAD_PAD ); // pad is the default
818 }
820 /**
821 * Clears the gradient's svg:stop children from its repr.
822 */
823 void
824 sp_gradient_repr_clear_vector(SPGradient *gr)
825 {
826 Inkscape::XML::Node *repr = SP_OBJECT_REPR(gr);
828 /* Collect stops from original repr */
829 GSList *sl = NULL;
830 for (Inkscape::XML::Node *child = repr->firstChild() ; child != NULL; child = child->next() ) {
831 if (!strcmp(child->name(), "svg:stop")) {
832 sl = g_slist_prepend(sl, child);
833 }
834 }
835 /* Remove all stops */
836 while (sl) {
837 /** \todo
838 * fixme: This should work, unless we make gradient
839 * into generic group.
840 */
841 sp_repr_unparent((Inkscape::XML::Node *)sl->data);
842 sl = g_slist_remove(sl, sl->data);
843 }
844 }
846 /**
847 * Writes the gradient's internal vector (whether from its own stops, or
848 * inherited from refs) into the gradient repr as svg:stop elements.
849 */
850 void
851 sp_gradient_repr_write_vector(SPGradient *gr)
852 {
853 g_return_if_fail(gr != NULL);
854 g_return_if_fail(SP_IS_GRADIENT(gr));
856 Inkscape::XML::Node *repr = SP_OBJECT_REPR(gr);
858 /* We have to be careful, as vector may be our own, so construct repr list at first */
859 GSList *cl = NULL;
861 for (guint i = 0; i < gr->vector.stops.size(); i++) {
862 Inkscape::CSSOStringStream os;
863 Inkscape::XML::Node *child = sp_repr_new("svg:stop");
864 sp_repr_set_css_double(child, "offset", gr->vector.stops[i].offset);
865 /* strictly speaking, offset an SVG <number> rather than a CSS one, but exponents make no
866 * sense for offset proportions. */
867 gchar c[64];
868 sp_svg_write_color(c, 64, sp_color_get_rgba32_ualpha(&gr->vector.stops[i].color, 0x00));
869 os << "stop-color:" << c << ";stop-opacity:" << gr->vector.stops[i].opacity;
870 child->setAttribute("style", os.str().c_str());
871 /* Order will be reversed here */
872 cl = g_slist_prepend(cl, child);
873 }
875 sp_gradient_repr_clear_vector(gr);
877 /* And insert new children from list */
878 while (cl) {
879 Inkscape::XML::Node *child = static_cast<Inkscape::XML::Node *>(cl->data);
880 repr->addChild(child, NULL);
881 Inkscape::GC::release(child);
882 cl = g_slist_remove(cl, child);
883 }
884 }
887 static void
888 gradient_ref_modified(SPObject *href, guint flags, SPGradient *gradient)
889 {
890 if (sp_gradient_invalidate_vector(gradient)) {
891 SP_OBJECT(gradient)->requestModified(SP_OBJECT_MODIFIED_FLAG);
892 /* Conditional to avoid causing infinite loop if there's a cycle in the href chain. */
893 }
894 }
896 /** Return true iff change made. */
897 static bool
898 sp_gradient_invalidate_vector(SPGradient *gr)
899 {
900 bool ret = false;
902 if (gr->color != NULL) {
903 g_free(gr->color);
904 gr->color = NULL;
905 ret = true;
906 }
908 if (gr->vector.built) {
909 gr->vector.built = false;
910 gr->vector.stops.clear();
911 ret = true;
912 }
914 return ret;
915 }
917 /** Creates normalized color vector */
918 static void
919 sp_gradient_rebuild_vector(SPGradient *gr)
920 {
921 gint len = 0;
922 for ( SPObject *child = sp_object_first_child(SP_OBJECT(gr)) ;
923 child != NULL ;
924 child = SP_OBJECT_NEXT(child) ) {
925 if (SP_IS_STOP(child)) {
926 len ++;
927 }
928 }
930 gr->has_stops = (len != 0);
932 gr->vector.stops.clear();
934 SPGradient *ref = gr->ref->getObject();
935 if ( !gr->has_stops && ref ) {
936 /* Copy vector from referenced gradient */
937 gr->vector.built = true; // Prevent infinite recursion.
938 sp_gradient_ensure_vector(ref);
939 if (!ref->vector.stops.empty()) {
940 gr->vector.built = ref->vector.built;
941 gr->vector.stops.assign(ref->vector.stops.begin(), ref->vector.stops.end());
942 return;
943 }
944 }
946 for (SPObject *child = sp_object_first_child(SP_OBJECT(gr)) ;
947 child != NULL;
948 child = SP_OBJECT_NEXT(child) ) {
949 if (SP_IS_STOP(child)) {
950 SPStop *stop = SP_STOP(child);
952 SPGradientStop gstop;
953 if (gr->vector.stops.size() > 0) {
954 // "Each gradient offset value is required to be equal to or greater than the
955 // previous gradient stop's offset value. If a given gradient stop's offset
956 // value is not equal to or greater than all previous offset values, then the
957 // offset value is adjusted to be equal to the largest of all previous offset
958 // values."
959 gstop.offset = MAX(stop->offset, gr->vector.stops.back().offset);
960 } else {
961 gstop.offset = stop->offset;
962 }
964 // "Gradient offset values less than 0 (or less than 0%) are rounded up to
965 // 0%. Gradient offset values greater than 1 (or greater than 100%) are rounded
966 // down to 100%."
967 gstop.offset = CLAMP(gstop.offset, 0, 1);
969 gstop.color = sp_stop_get_color(stop);
970 gstop.opacity = stop->opacity;
972 gr->vector.stops.push_back(gstop);
973 }
974 }
976 // Normalize per section 13.2.4 of SVG 1.1.
977 if (gr->vector.stops.size() == 0) {
978 /* "If no stops are defined, then painting shall occur as if 'none' were specified as the
979 * paint style."
980 */
981 {
982 SPGradientStop gstop;
983 gstop.offset = 0.0;
984 sp_color_set_rgb_rgba32(&gstop.color, 0x00000000);
985 gstop.opacity = 0.0;
986 gr->vector.stops.push_back(gstop);
987 }
988 {
989 SPGradientStop gstop;
990 gstop.offset = 1.0;
991 sp_color_set_rgb_rgba32(&gstop.color, 0x00000000);
992 gstop.opacity = 0.0;
993 gr->vector.stops.push_back(gstop);
994 }
995 } else {
996 /* "If one stop is defined, then paint with the solid color fill using the color defined
997 * for that gradient stop."
998 */
999 if (gr->vector.stops.front().offset > 0.0) {
1000 // If the first one is not at 0, then insert a copy of the first at 0.
1001 SPGradientStop gstop;
1002 gstop.offset = 0.0;
1003 sp_color_copy(&gstop.color, &gr->vector.stops.front().color);
1004 gstop.opacity = gr->vector.stops.front().opacity;
1005 gr->vector.stops.insert(gr->vector.stops.begin(), gstop);
1006 }
1007 if (gr->vector.stops.back().offset < 1.0) {
1008 // If the last one is not at 1, then insert a copy of the last at 1.
1009 SPGradientStop gstop;
1010 gstop.offset = 1.0;
1011 sp_color_copy(&gstop.color, &gr->vector.stops.back().color);
1012 gstop.opacity = gr->vector.stops.back().opacity;
1013 gr->vector.stops.push_back(gstop);
1014 }
1015 }
1017 gr->vector.built = true;
1018 }
1020 /**
1021 * The gradient's color array is newly created and set up from vector.
1022 */
1023 void
1024 sp_gradient_ensure_colors(SPGradient *gr)
1025 {
1026 if (!gr->vector.built) {
1027 sp_gradient_rebuild_vector(gr);
1028 }
1029 g_return_if_fail(!gr->vector.stops.empty());
1031 /// \todo Where is the memory freed?
1032 if (!gr->color) {
1033 gr->color = g_new(guchar, 4 * NCOLORS);
1034 }
1036 for (guint i = 0; i < gr->vector.stops.size() - 1; i++) {
1037 guint32 color = sp_color_get_rgba32_falpha(&gr->vector.stops[i].color,
1038 gr->vector.stops[i].opacity);
1039 gint r0 = (color >> 24) & 0xff;
1040 gint g0 = (color >> 16) & 0xff;
1041 gint b0 = (color >> 8) & 0xff;
1042 gint a0 = color & 0xff;
1043 color = sp_color_get_rgba32_falpha(&gr->vector.stops[i + 1].color,
1044 gr->vector.stops[i + 1].opacity);
1045 gint r1 = (color >> 24) & 0xff;
1046 gint g1 = (color >> 16) & 0xff;
1047 gint b1 = (color >> 8) & 0xff;
1048 gint a1 = color & 0xff;
1049 gint o0 = (gint) floor(gr->vector.stops[i].offset * (NCOLORS - 0.001));
1050 gint o1 = (gint) floor(gr->vector.stops[i + 1].offset * (NCOLORS - 0.001));
1051 if (o1 > o0) {
1052 gint dr = ((r1 - r0) << 16) / (o1 - o0);
1053 gint dg = ((g1 - g0) << 16) / (o1 - o0);
1054 gint db = ((b1 - b0) << 16) / (o1 - o0);
1055 gint da = ((a1 - a0) << 16) / (o1 - o0);
1056 gint r = r0 << 16;
1057 gint g = g0 << 16;
1058 gint b = b0 << 16;
1059 gint a = a0 << 16;
1060 for (int j = o0; j < o1 + 1; j++) {
1061 gr->color[4 * j] = r >> 16;
1062 gr->color[4 * j + 1] = g >> 16;
1063 gr->color[4 * j + 2] = b >> 16;
1064 gr->color[4 * j + 3] = a >> 16;
1065 r += dr;
1066 g += dg;
1067 b += db;
1068 a += da;
1069 }
1070 }
1071 }
1072 }
1074 /**
1075 * Renders gradient vector to buffer as line.
1076 *
1077 * RGB buffer background should be set up beforehand.
1078 *
1079 * @param len,width,height,rowstride Buffer parameters (1 or 2 dimensional).
1080 * @param span Full integer width of requested gradient.
1081 * @param pos Buffer starting position in span.
1082 */
1083 static void
1084 sp_gradient_render_vector_line_rgba(SPGradient *const gradient, guchar *buf,
1085 gint const len, gint const pos, gint const span)
1086 {
1087 g_return_if_fail(gradient != NULL);
1088 g_return_if_fail(SP_IS_GRADIENT(gradient));
1089 g_return_if_fail(buf != NULL);
1090 g_return_if_fail(len > 0);
1091 g_return_if_fail(pos >= 0);
1092 g_return_if_fail(pos + len <= span);
1093 g_return_if_fail(span > 0);
1095 if (!gradient->color) {
1096 sp_gradient_ensure_colors(gradient);
1097 }
1099 gint idx = (pos * 1024 << 8) / span;
1100 gint didx = (1024 << 8) / span;
1102 for (gint x = 0; x < len; x++) {
1103 /// \todo Can this be done with 4 byte copies?
1104 *buf++ = gradient->color[4 * (idx >> 8)];
1105 *buf++ = gradient->color[4 * (idx >> 8) + 1];
1106 *buf++ = gradient->color[4 * (idx >> 8) + 2];
1107 *buf++ = gradient->color[4 * (idx >> 8) + 3];
1108 idx += didx;
1109 }
1110 }
1112 /**
1113 * Render rectangular RGBA area from gradient vector.
1114 */
1115 void
1116 sp_gradient_render_vector_block_rgba(SPGradient *const gradient, guchar *buf,
1117 gint const width, gint const height, gint const rowstride,
1118 gint const pos, gint const span, bool const horizontal)
1119 {
1120 g_return_if_fail(gradient != NULL);
1121 g_return_if_fail(SP_IS_GRADIENT(gradient));
1122 g_return_if_fail(buf != NULL);
1123 g_return_if_fail(width > 0);
1124 g_return_if_fail(height > 0);
1125 g_return_if_fail(pos >= 0);
1126 g_return_if_fail((horizontal && (pos + width <= span)) || (!horizontal && (pos + height <= span)));
1127 g_return_if_fail(span > 0);
1129 if (horizontal) {
1130 sp_gradient_render_vector_line_rgba(gradient, buf, width, pos, span);
1131 for (gint y = 1; y < height; y++) {
1132 memcpy(buf + y * rowstride, buf, 4 * width);
1133 }
1134 } else {
1135 guchar *tmp = (guchar *)alloca(4 * height);
1136 sp_gradient_render_vector_line_rgba(gradient, tmp, height, pos, span);
1137 for (gint y = 0; y < height; y++) {
1138 guchar *b = buf + y * rowstride;
1139 for (gint x = 0; x < width; x++) {
1140 *b++ = tmp[0];
1141 *b++ = tmp[1];
1142 *b++ = tmp[2];
1143 *b++ = tmp[3];
1144 }
1145 tmp += 4;
1146 }
1147 }
1148 }
1150 /**
1151 * Render rectangular RGB area from gradient vector.
1152 */
1153 void
1154 sp_gradient_render_vector_block_rgb(SPGradient *gradient, guchar *buf,
1155 gint const width, gint const height, gint const rowstride,
1156 gint const pos, gint const span, bool const horizontal)
1157 {
1158 g_return_if_fail(gradient != NULL);
1159 g_return_if_fail(SP_IS_GRADIENT(gradient));
1160 g_return_if_fail(buf != NULL);
1161 g_return_if_fail(width > 0);
1162 g_return_if_fail(height > 0);
1163 g_return_if_fail(pos >= 0);
1164 g_return_if_fail((horizontal && (pos + width <= span)) || (!horizontal && (pos + height <= span)));
1165 g_return_if_fail(span > 0);
1167 if (horizontal) {
1168 guchar *tmp = (guchar*)alloca(4 * width);
1169 sp_gradient_render_vector_line_rgba(gradient, tmp, width, pos, span);
1170 for (gint y = 0; y < height; y++) {
1171 guchar *t = tmp;
1172 for (gint x = 0; x < width; x++) {
1173 gint a = t[3];
1174 gint fc = (t[0] - buf[0]) * a;
1175 buf[0] = buf[0] + ((fc + (fc >> 8) + 0x80) >> 8);
1176 fc = (t[1] - buf[1]) * a;
1177 buf[1] = buf[1] + ((fc + (fc >> 8) + 0x80) >> 8);
1178 fc = (t[2] - buf[2]) * a;
1179 buf[2] = buf[2] + ((fc + (fc >> 8) + 0x80) >> 8);
1180 buf += 3;
1181 t += 4;
1182 }
1183 }
1184 } else {
1185 guchar *tmp = (guchar*)alloca(4 * height);
1186 sp_gradient_render_vector_line_rgba(gradient, tmp, height, pos, span);
1187 for (gint y = 0; y < height; y++) {
1188 guchar *t = tmp + 4 * y;
1189 for (gint x = 0; x < width; x++) {
1190 gint a = t[3];
1191 gint fc = (t[0] - buf[0]) * a;
1192 buf[0] = buf[0] + ((fc + (fc >> 8) + 0x80) >> 8);
1193 fc = (t[1] - buf[1]) * a;
1194 buf[1] = buf[1] + ((fc + (fc >> 8) + 0x80) >> 8);
1195 fc = (t[2] - buf[2]) * a;
1196 buf[2] = buf[2] + ((fc + (fc >> 8) + 0x80) >> 8);
1197 }
1198 }
1199 }
1200 }
1202 NR::Matrix
1203 sp_gradient_get_g2d_matrix(SPGradient const *gr, NR::Matrix const &ctm, NR::Rect const &bbox)
1204 {
1205 if (gr->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) {
1206 return ( NR::scale(bbox.dimensions())
1207 * NR::translate(bbox.min())
1208 * ctm );
1209 } else {
1210 return ctm;
1211 }
1212 }
1214 NR::Matrix
1215 sp_gradient_get_gs2d_matrix(SPGradient const *gr, NR::Matrix const &ctm, NR::Rect const &bbox)
1216 {
1217 if (gr->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) {
1218 return ( gr->gradientTransform
1219 * NR::scale(bbox.dimensions())
1220 * NR::translate(bbox.min())
1221 * ctm );
1222 } else {
1223 return gr->gradientTransform * ctm;
1224 }
1225 }
1227 void
1228 sp_gradient_set_gs2d_matrix(SPGradient *gr, NR::Matrix const &ctm,
1229 NR::Rect const &bbox, NR::Matrix const &gs2d)
1230 {
1231 gr->gradientTransform = gs2d / ctm;
1232 if ( gr->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX ) {
1233 gr->gradientTransform = ( gr->gradientTransform
1234 / NR::translate(bbox.min())
1235 / NR::scale(bbox.dimensions()) );
1236 }
1237 gr->gradientTransform_set = TRUE;
1239 SP_OBJECT(gr)->requestModified(SP_OBJECT_MODIFIED_FLAG);
1240 }
1242 /*
1243 * Linear Gradient
1244 */
1246 class SPLGPainter;
1248 /// A context with linear gradient, painter, and gradient renderer.
1249 struct SPLGPainter {
1250 SPPainter painter;
1251 SPLinearGradient *lg;
1253 NRLGradientRenderer lgr;
1254 };
1256 static void sp_lineargradient_class_init(SPLinearGradientClass *klass);
1257 static void sp_lineargradient_init(SPLinearGradient *lg);
1259 static void sp_lineargradient_build(SPObject *object,
1260 SPDocument *document,
1261 Inkscape::XML::Node *repr);
1262 static void sp_lineargradient_set(SPObject *object, unsigned key, gchar const *value);
1263 static Inkscape::XML::Node *sp_lineargradient_write(SPObject *object, Inkscape::XML::Node *repr,
1264 guint flags);
1266 static SPPainter *sp_lineargradient_painter_new(SPPaintServer *ps,
1267 NR::Matrix const &full_transform,
1268 NR::Matrix const &parent_transform,
1269 NRRect const *bbox);
1270 static void sp_lineargradient_painter_free(SPPaintServer *ps, SPPainter *painter);
1272 static void sp_lg_fill(SPPainter *painter, NRPixBlock *pb);
1274 static SPGradientClass *lg_parent_class;
1276 /**
1277 * Register SPLinearGradient class and return its type.
1278 */
1279 GType
1280 sp_lineargradient_get_type()
1281 {
1282 static GType type = 0;
1283 if (!type) {
1284 GTypeInfo info = {
1285 sizeof(SPLinearGradientClass),
1286 NULL, NULL,
1287 (GClassInitFunc) sp_lineargradient_class_init,
1288 NULL, NULL,
1289 sizeof(SPLinearGradient),
1290 16,
1291 (GInstanceInitFunc) sp_lineargradient_init,
1292 NULL, /* value_table */
1293 };
1294 type = g_type_register_static(SP_TYPE_GRADIENT, "SPLinearGradient", &info, (GTypeFlags)0);
1295 }
1296 return type;
1297 }
1299 /**
1300 * SPLinearGradient vtable initialization.
1301 */
1302 static void sp_lineargradient_class_init(SPLinearGradientClass *klass)
1303 {
1304 SPObjectClass *sp_object_class = (SPObjectClass *) klass;
1305 SPPaintServerClass *ps_class = (SPPaintServerClass *) klass;
1307 lg_parent_class = (SPGradientClass*)g_type_class_ref(SP_TYPE_GRADIENT);
1309 sp_object_class->build = sp_lineargradient_build;
1310 sp_object_class->set = sp_lineargradient_set;
1311 sp_object_class->write = sp_lineargradient_write;
1313 ps_class->painter_new = sp_lineargradient_painter_new;
1314 ps_class->painter_free = sp_lineargradient_painter_free;
1315 }
1317 /**
1318 * Callback for SPLinearGradient object initialization.
1319 */
1320 static void sp_lineargradient_init(SPLinearGradient *lg)
1321 {
1322 lg->x1.unset(SVGLength::PERCENT, 0.0, 0.0);
1323 lg->y1.unset(SVGLength::PERCENT, 0.5, 0.5);
1324 lg->x2.unset(SVGLength::PERCENT, 1.0, 1.0);
1325 lg->y2.unset(SVGLength::PERCENT, 0.5, 0.5);
1326 }
1328 /**
1329 * Callback: set attributes from associated repr.
1330 */
1331 static void sp_lineargradient_build(SPObject *object,
1332 SPDocument *document,
1333 Inkscape::XML::Node *repr)
1334 {
1335 if (((SPObjectClass *) lg_parent_class)->build)
1336 (* ((SPObjectClass *) lg_parent_class)->build)(object, document, repr);
1338 sp_object_read_attr(object, "x1");
1339 sp_object_read_attr(object, "y1");
1340 sp_object_read_attr(object, "x2");
1341 sp_object_read_attr(object, "y2");
1342 }
1344 /**
1345 * Callback: set attribute.
1346 */
1347 static void
1348 sp_lineargradient_set(SPObject *object, unsigned key, gchar const *value)
1349 {
1350 SPLinearGradient *lg = SP_LINEARGRADIENT(object);
1352 switch (key) {
1353 case SP_ATTR_X1:
1354 lg->x1.readOrUnset(value, SVGLength::PERCENT, 0.0, 0.0);
1355 object->requestModified(SP_OBJECT_MODIFIED_FLAG);
1356 break;
1357 case SP_ATTR_Y1:
1358 lg->y1.readOrUnset(value, SVGLength::PERCENT, 0.5, 0.5);
1359 object->requestModified(SP_OBJECT_MODIFIED_FLAG);
1360 break;
1361 case SP_ATTR_X2:
1362 lg->x2.readOrUnset(value, SVGLength::PERCENT, 1.0, 1.0);
1363 object->requestModified(SP_OBJECT_MODIFIED_FLAG);
1364 break;
1365 case SP_ATTR_Y2:
1366 lg->y2.readOrUnset(value, SVGLength::PERCENT, 0.5, 0.5);
1367 object->requestModified(SP_OBJECT_MODIFIED_FLAG);
1368 break;
1369 default:
1370 if (((SPObjectClass *) lg_parent_class)->set)
1371 (* ((SPObjectClass *) lg_parent_class)->set)(object, key, value);
1372 break;
1373 }
1374 }
1376 /**
1377 * Callback: write attributes to associated repr.
1378 */
1379 static Inkscape::XML::Node *
1380 sp_lineargradient_write(SPObject *object, Inkscape::XML::Node *repr, guint flags)
1381 {
1382 SPLinearGradient *lg = SP_LINEARGRADIENT(object);
1384 if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
1385 repr = sp_repr_new("svg:linearGradient");
1386 }
1388 if ((flags & SP_OBJECT_WRITE_ALL) || lg->x1._set)
1389 sp_repr_set_svg_double(repr, "x1", lg->x1.computed);
1390 if ((flags & SP_OBJECT_WRITE_ALL) || lg->y1._set)
1391 sp_repr_set_svg_double(repr, "y1", lg->y1.computed);
1392 if ((flags & SP_OBJECT_WRITE_ALL) || lg->x2._set)
1393 sp_repr_set_svg_double(repr, "x2", lg->x2.computed);
1394 if ((flags & SP_OBJECT_WRITE_ALL) || lg->y2._set)
1395 sp_repr_set_svg_double(repr, "y2", lg->y2.computed);
1397 if (((SPObjectClass *) lg_parent_class)->write)
1398 (* ((SPObjectClass *) lg_parent_class)->write)(object, repr, flags);
1400 return repr;
1401 }
1403 /**
1404 * Create linear gradient context.
1405 *
1406 * Basically we have to deal with transformations
1407 *
1408 * 1) color2norm - maps point in (0,NCOLORS) vector to (0,1) vector
1409 * 2) norm2pos - maps (0,1) vector to x1,y1 - x2,y2
1410 * 2) gradientTransform
1411 * 3) bbox2user
1412 * 4) ctm == userspace to pixel grid
1413 *
1414 * See also (*) in sp-pattern about why we may need parent_transform.
1415 *
1416 * \todo (point 1 above) fixme: I do not know how to deal with start > 0
1417 * and end < 1.
1418 */
1419 static SPPainter *
1420 sp_lineargradient_painter_new(SPPaintServer *ps,
1421 NR::Matrix const &full_transform,
1422 NR::Matrix const &parent_transform,
1423 NRRect const *bbox)
1424 {
1425 SPLinearGradient *lg = SP_LINEARGRADIENT(ps);
1426 SPGradient *gr = SP_GRADIENT(ps);
1428 if (!gr->color) sp_gradient_ensure_colors(gr);
1430 SPLGPainter *lgp = g_new(SPLGPainter, 1);
1432 lgp->painter.type = SP_PAINTER_IND;
1433 lgp->painter.fill = sp_lg_fill;
1435 lgp->lg = lg;
1437 /** \todo
1438 * Technically speaking, we map NCOLORS on line [start,end] onto line
1439 * [0,1]. I almost think we should fill color array start and end in
1440 * that case. The alternative would be to leave these just empty garbage
1441 * or something similar. Originally I had 1023.9999 here - not sure
1442 * whether we have really to cut out ceil int (Lauris).
1443 */
1444 NR::Matrix color2norm(NR::identity());
1445 NR::Matrix color2px;
1446 if (gr->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) {
1447 NR::Matrix norm2pos(NR::identity());
1449 /* BBox to user coordinate system */
1450 NR::Matrix bbox2user(bbox->x1 - bbox->x0, 0, 0, bbox->y1 - bbox->y0, bbox->x0, bbox->y0);
1452 NR::Matrix color2pos = color2norm * norm2pos;
1453 NR::Matrix color2tpos = color2pos * gr->gradientTransform;
1454 NR::Matrix color2user = color2tpos * bbox2user;
1455 color2px = color2user * full_transform;
1457 } else {
1458 /* Problem: What to do, if we have mixed lengths and percentages? */
1459 /* Currently we do ignore percentages at all, but that is not good (lauris) */
1461 NR::Matrix norm2pos(NR::identity());
1462 NR::Matrix color2pos = color2norm * norm2pos;
1463 NR::Matrix color2tpos = color2pos * gr->gradientTransform;
1464 color2px = color2tpos * full_transform;
1466 }
1468 NRMatrix v2px;
1469 color2px.copyto(&v2px);
1471 nr_lgradient_renderer_setup(&lgp->lgr, gr->color, sp_gradient_get_spread(gr), &v2px,
1472 lg->x1.computed, lg->y1.computed,
1473 lg->x2.computed, lg->y2.computed);
1475 return (SPPainter *) lgp;
1476 }
1478 static void
1479 sp_lineargradient_painter_free(SPPaintServer *ps, SPPainter *painter)
1480 {
1481 g_free(painter);
1482 }
1484 /**
1485 * Directly set properties of linear gradient and request modified.
1486 */
1487 void
1488 sp_lineargradient_set_position(SPLinearGradient *lg,
1489 gdouble x1, gdouble y1,
1490 gdouble x2, gdouble y2)
1491 {
1492 g_return_if_fail(lg != NULL);
1493 g_return_if_fail(SP_IS_LINEARGRADIENT(lg));
1495 /* fixme: units? (Lauris) */
1496 lg->x1.set(SVGLength::NONE, x1, x1);
1497 lg->y1.set(SVGLength::NONE, y1, y1);
1498 lg->x2.set(SVGLength::NONE, x2, x2);
1499 lg->y2.set(SVGLength::NONE, y2, y2);
1501 SP_OBJECT(lg)->requestModified(SP_OBJECT_MODIFIED_FLAG);
1502 }
1504 /**
1505 * Callback when linear gradient object is rendered.
1506 */
1507 static void
1508 sp_lg_fill(SPPainter *painter, NRPixBlock *pb)
1509 {
1510 SPLGPainter *lgp = (SPLGPainter *) painter;
1512 if (lgp->lg->color == NULL) {
1513 sp_gradient_ensure_colors (lgp->lg);
1514 lgp->lgr.vector = lgp->lg->color;
1515 }
1517 nr_render((NRRenderer *) &lgp->lgr, pb, NULL);
1518 }
1520 /*
1521 * Radial Gradient
1522 */
1524 class SPRGPainter;
1526 /// A context with radial gradient, painter, and gradient renderer.
1527 struct SPRGPainter {
1528 SPPainter painter;
1529 SPRadialGradient *rg;
1530 NRRGradientRenderer rgr;
1531 };
1533 static void sp_radialgradient_class_init(SPRadialGradientClass *klass);
1534 static void sp_radialgradient_init(SPRadialGradient *rg);
1536 static void sp_radialgradient_build(SPObject *object,
1537 SPDocument *document,
1538 Inkscape::XML::Node *repr);
1539 static void sp_radialgradient_set(SPObject *object, unsigned key, gchar const *value);
1540 static Inkscape::XML::Node *sp_radialgradient_write(SPObject *object, Inkscape::XML::Node *repr,
1541 guint flags);
1543 static SPPainter *sp_radialgradient_painter_new(SPPaintServer *ps,
1544 NR::Matrix const &full_transform,
1545 NR::Matrix const &parent_transform,
1546 NRRect const *bbox);
1547 static void sp_radialgradient_painter_free(SPPaintServer *ps, SPPainter *painter);
1549 static void sp_rg_fill(SPPainter *painter, NRPixBlock *pb);
1551 static SPGradientClass *rg_parent_class;
1553 /**
1554 * Register SPRadialGradient class and return its type.
1555 */
1556 GType
1557 sp_radialgradient_get_type()
1558 {
1559 static GType type = 0;
1560 if (!type) {
1561 GTypeInfo info = {
1562 sizeof(SPRadialGradientClass),
1563 NULL, NULL,
1564 (GClassInitFunc) sp_radialgradient_class_init,
1565 NULL, NULL,
1566 sizeof(SPRadialGradient),
1567 16,
1568 (GInstanceInitFunc) sp_radialgradient_init,
1569 NULL, /* value_table */
1570 };
1571 type = g_type_register_static(SP_TYPE_GRADIENT, "SPRadialGradient", &info, (GTypeFlags)0);
1572 }
1573 return type;
1574 }
1576 /**
1577 * SPRadialGradient vtable initialization.
1578 */
1579 static void sp_radialgradient_class_init(SPRadialGradientClass *klass)
1580 {
1581 SPObjectClass *sp_object_class = (SPObjectClass *) klass;
1582 SPPaintServerClass *ps_class = (SPPaintServerClass *) klass;
1584 rg_parent_class = (SPGradientClass*)g_type_class_ref(SP_TYPE_GRADIENT);
1586 sp_object_class->build = sp_radialgradient_build;
1587 sp_object_class->set = sp_radialgradient_set;
1588 sp_object_class->write = sp_radialgradient_write;
1590 ps_class->painter_new = sp_radialgradient_painter_new;
1591 ps_class->painter_free = sp_radialgradient_painter_free;
1592 }
1594 /**
1595 * Callback for SPRadialGradient object initialization.
1596 */
1597 static void
1598 sp_radialgradient_init(SPRadialGradient *rg)
1599 {
1600 rg->cx.unset(SVGLength::PERCENT, 0.5, 0.5);
1601 rg->cy.unset(SVGLength::PERCENT, 0.5, 0.5);
1602 rg->r.unset(SVGLength::PERCENT, 0.5, 0.5);
1603 rg->fx.unset(SVGLength::PERCENT, 0.5, 0.5);
1604 rg->fy.unset(SVGLength::PERCENT, 0.5, 0.5);
1605 }
1607 /**
1608 * Set radial gradient attributes from associated repr.
1609 */
1610 static void
1611 sp_radialgradient_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
1612 {
1613 if (((SPObjectClass *) rg_parent_class)->build)
1614 (* ((SPObjectClass *) rg_parent_class)->build)(object, document, repr);
1616 sp_object_read_attr(object, "cx");
1617 sp_object_read_attr(object, "cy");
1618 sp_object_read_attr(object, "r");
1619 sp_object_read_attr(object, "fx");
1620 sp_object_read_attr(object, "fy");
1621 }
1623 /**
1624 * Set radial gradient attribute.
1625 */
1626 static void
1627 sp_radialgradient_set(SPObject *object, unsigned key, gchar const *value)
1628 {
1629 SPRadialGradient *rg = SP_RADIALGRADIENT(object);
1631 switch (key) {
1632 case SP_ATTR_CX:
1633 if (!rg->cx.read(value)) {
1634 rg->cx.unset(SVGLength::PERCENT, 0.5, 0.5);
1635 }
1636 if (!rg->fx._set) {
1637 rg->fx.value = rg->cx.value;
1638 rg->fx.computed = rg->cx.computed;
1639 }
1640 object->requestModified(SP_OBJECT_MODIFIED_FLAG);
1641 break;
1642 case SP_ATTR_CY:
1643 if (!rg->cy.read(value)) {
1644 rg->cy.unset(SVGLength::PERCENT, 0.5, 0.5);
1645 }
1646 if (!rg->fy._set) {
1647 rg->fy.value = rg->cy.value;
1648 rg->fy.computed = rg->cy.computed;
1649 }
1650 object->requestModified(SP_OBJECT_MODIFIED_FLAG);
1651 break;
1652 case SP_ATTR_R:
1653 if (!rg->r.read(value)) {
1654 rg->r.unset(SVGLength::PERCENT, 0.5, 0.5);
1655 }
1656 object->requestModified(SP_OBJECT_MODIFIED_FLAG);
1657 break;
1658 case SP_ATTR_FX:
1659 if (!rg->fx.read(value)) {
1660 rg->fx.unset(rg->cx.unit, rg->cx.value, rg->cx.computed);
1661 }
1662 object->requestModified(SP_OBJECT_MODIFIED_FLAG);
1663 break;
1664 case SP_ATTR_FY:
1665 if (!rg->fy.read(value)) {
1666 rg->fy.unset(rg->cy.unit, rg->cy.value, rg->cy.computed);
1667 }
1668 object->requestModified(SP_OBJECT_MODIFIED_FLAG);
1669 break;
1670 default:
1671 if (((SPObjectClass *) rg_parent_class)->set)
1672 ((SPObjectClass *) rg_parent_class)->set(object, key, value);
1673 break;
1674 }
1675 }
1677 /**
1678 * Write radial gradient attributes to associated repr.
1679 */
1680 static Inkscape::XML::Node *
1681 sp_radialgradient_write(SPObject *object, Inkscape::XML::Node *repr, guint flags)
1682 {
1683 SPRadialGradient *rg = SP_RADIALGRADIENT(object);
1685 if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
1686 repr = sp_repr_new("svg:radialGradient");
1687 }
1689 if ((flags & SP_OBJECT_WRITE_ALL) || rg->cx._set) sp_repr_set_svg_double(repr, "cx", rg->cx.computed);
1690 if ((flags & SP_OBJECT_WRITE_ALL) || rg->cy._set) sp_repr_set_svg_double(repr, "cy", rg->cy.computed);
1691 if ((flags & SP_OBJECT_WRITE_ALL) || rg->r._set) sp_repr_set_svg_double(repr, "r", rg->r.computed);
1692 if ((flags & SP_OBJECT_WRITE_ALL) || rg->fx._set) sp_repr_set_svg_double(repr, "fx", rg->fx.computed);
1693 if ((flags & SP_OBJECT_WRITE_ALL) || rg->fy._set) sp_repr_set_svg_double(repr, "fy", rg->fy.computed);
1695 if (((SPObjectClass *) rg_parent_class)->write)
1696 (* ((SPObjectClass *) rg_parent_class)->write)(object, repr, flags);
1698 return repr;
1699 }
1701 /**
1702 * Create radial gradient context.
1703 */
1704 static SPPainter *
1705 sp_radialgradient_painter_new(SPPaintServer *ps,
1706 NR::Matrix const &full_transform,
1707 NR::Matrix const &parent_transform,
1708 NRRect const *bbox)
1709 {
1710 SPRadialGradient *rg = SP_RADIALGRADIENT(ps);
1711 SPGradient *gr = SP_GRADIENT(ps);
1713 if (!gr->color) sp_gradient_ensure_colors(gr);
1715 SPRGPainter *rgp = g_new(SPRGPainter, 1);
1717 rgp->painter.type = SP_PAINTER_IND;
1718 rgp->painter.fill = sp_rg_fill;
1720 rgp->rg = rg;
1722 NR::Matrix gs2px;
1724 if (gr->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) {
1725 /** \todo
1726 * fixme: We may try to normalize here too, look at
1727 * linearGradient (Lauris)
1728 */
1730 /* BBox to user coordinate system */
1731 NR::Matrix bbox2user(bbox->x1 - bbox->x0, 0, 0, bbox->y1 - bbox->y0, bbox->x0, bbox->y0);
1733 NR::Matrix gs2user = gr->gradientTransform * bbox2user;
1735 gs2px = gs2user * full_transform;
1736 } else {
1737 /** \todo
1738 * Problem: What to do, if we have mixed lengths and percentages?
1739 * Currently we do ignore percentages at all, but that is not
1740 * good (lauris)
1741 */
1743 gs2px = gr->gradientTransform * full_transform;
1744 }
1746 NRMatrix gs2px_nr;
1747 gs2px.copyto(&gs2px_nr);
1749 nr_rgradient_renderer_setup(&rgp->rgr, gr->color, sp_gradient_get_spread(gr),
1750 &gs2px_nr,
1751 rg->cx.computed, rg->cy.computed,
1752 rg->fx.computed, rg->fy.computed,
1753 rg->r.computed);
1755 return (SPPainter *) rgp;
1756 }
1758 static void
1759 sp_radialgradient_painter_free(SPPaintServer *ps, SPPainter *painter)
1760 {
1761 g_free(painter);
1762 }
1764 /**
1765 * Directly set properties of radial gradient and request modified.
1766 */
1767 void
1768 sp_radialgradient_set_position(SPRadialGradient *rg,
1769 gdouble cx, gdouble cy, gdouble fx, gdouble fy, gdouble r)
1770 {
1771 g_return_if_fail(rg != NULL);
1772 g_return_if_fail(SP_IS_RADIALGRADIENT(rg));
1774 /* fixme: units? (Lauris) */
1775 rg->cx.set(SVGLength::NONE, cx, cx);
1776 rg->cy.set(SVGLength::NONE, cy, cy);
1777 rg->fx.set(SVGLength::NONE, fx, fx);
1778 rg->fy.set(SVGLength::NONE, fy, fy);
1779 rg->r.set(SVGLength::NONE, r, r);
1781 SP_OBJECT(rg)->requestModified(SP_OBJECT_MODIFIED_FLAG);
1782 }
1784 /**
1785 * Callback when radial gradient object is rendered.
1786 */
1787 static void
1788 sp_rg_fill(SPPainter *painter, NRPixBlock *pb)
1789 {
1790 SPRGPainter *rgp = (SPRGPainter *) painter;
1792 if (rgp->rg->color == NULL) {
1793 sp_gradient_ensure_colors (rgp->rg);
1794 rgp->rgr.vector = rgp->rg->color;
1795 }
1797 nr_render((NRRenderer *) &rgp->rgr, pb, NULL);
1798 }
1800 /*
1801 Local Variables:
1802 mode:c++
1803 c-file-style:"stroustrup"
1804 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1805 indent-tabs-mode:nil
1806 fill-column:99
1807 End:
1808 */
1809 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :