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