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=sp_svg_transform_write(gr->gradientTransform);
665 repr->setAttribute("gradientTransform", c);
666 g_free(c);
667 }
669 if ((flags & SP_OBJECT_WRITE_ALL) || gr->spread_set) {
670 /* FIXME: Ensure that gr->spread is the inherited value
671 * if !gr->spread_set. Not currently happening: see sp_gradient_modified.
672 */
673 switch (gr->spread) {
674 case SP_GRADIENT_SPREAD_REFLECT:
675 repr->setAttribute("spreadMethod", "reflect");
676 break;
677 case SP_GRADIENT_SPREAD_REPEAT:
678 repr->setAttribute("spreadMethod", "repeat");
679 break;
680 default:
681 repr->setAttribute("spreadMethod", "pad");
682 break;
683 }
684 }
686 return repr;
687 }
689 /**
690 * Forces the vector to be built, if not present (i.e., changed).
691 *
692 * \pre SP_IS_GRADIENT(gradient).
693 */
694 void
695 sp_gradient_ensure_vector(SPGradient *gradient)
696 {
697 g_return_if_fail(gradient != NULL);
698 g_return_if_fail(SP_IS_GRADIENT(gradient));
700 if (!gradient->vector.built) {
701 sp_gradient_rebuild_vector(gradient);
702 }
703 }
705 /**
706 * Set units property of gradient and emit modified.
707 */
708 void
709 sp_gradient_set_units(SPGradient *gr, SPGradientUnits units)
710 {
711 if (units != gr->units) {
712 gr->units = units;
713 gr->units_set = TRUE;
714 SP_OBJECT(gr)->requestModified(SP_OBJECT_MODIFIED_FLAG);
715 }
716 }
718 /**
719 * Set spread property of gradient and emit modified.
720 */
721 void
722 sp_gradient_set_spread(SPGradient *gr, SPGradientSpread spread)
723 {
724 if (spread != gr->spread) {
725 gr->spread = spread;
726 gr->spread_set = TRUE;
727 SP_OBJECT(gr)->requestModified(SP_OBJECT_MODIFIED_FLAG);
728 }
729 }
731 /**
732 * Returns the first of {src, src-\>ref-\>getObject(),
733 * src-\>ref-\>getObject()-\>ref-\>getObject(),...}
734 * for which \a match is true, or NULL if none found.
735 *
736 * The raison d'être of this routine is that it correctly handles cycles in the href chain (e.g., if
737 * a gradient gives itself as its href, or if each of two gradients gives the other as its href).
738 *
739 * \pre SP_IS_GRADIENT(src).
740 */
741 static SPGradient *
742 chase_hrefs(SPGradient *const src, bool (*match)(SPGradient const *))
743 {
744 g_return_val_if_fail(SP_IS_GRADIENT(src), NULL);
746 /* Use a pair of pointers for detecting loops: p1 advances half as fast as p2. If there is a
747 loop, then once p1 has entered the loop, we'll detect it the next time the distance between
748 p1 and p2 is a multiple of the loop size. */
749 SPGradient *p1 = src, *p2 = src;
750 bool do1 = false;
751 for (;;) {
752 if (match(p2)) {
753 return p2;
754 }
756 p2 = p2->ref->getObject();
757 if (!p2) {
758 return p2;
759 }
760 if (do1) {
761 p1 = p1->ref->getObject();
762 }
763 do1 = !do1;
765 if ( p2 == p1 ) {
766 /* We've been here before, so return NULL to indicate that no matching gradient found
767 * in the chain. */
768 return NULL;
769 }
770 }
771 }
773 /**
774 * True if gradient has stops.
775 */
776 static bool
777 has_stops(SPGradient const *gr)
778 {
779 return SP_GRADIENT_HAS_STOPS(gr);
780 }
782 /**
783 * True if gradient has spread set.
784 */
785 static bool
786 has_spread_set(SPGradient const *gr)
787 {
788 return gr->spread_set;
789 }
792 /**
793 * Returns private vector of given gradient (the gradient at the end of the href chain which has
794 * stops), optionally normalizing it.
795 *
796 * \pre SP_IS_GRADIENT(gradient).
797 * \pre There exists a gradient in the chain that has stops.
798 */
799 SPGradient *
800 sp_gradient_get_vector(SPGradient *gradient, bool force_vector)
801 {
802 g_return_val_if_fail(gradient != NULL, NULL);
803 g_return_val_if_fail(SP_IS_GRADIENT(gradient), NULL);
805 SPGradient *const src = chase_hrefs(gradient, has_stops);
806 return ( force_vector
807 ? sp_gradient_ensure_vector_normalized(src)
808 : src );
809 }
811 /**
812 * Returns the effective spread of given gradient (climbing up the refs chain if needed).
813 *
814 * \pre SP_IS_GRADIENT(gradient).
815 */
816 SPGradientSpread
817 sp_gradient_get_spread(SPGradient *gradient)
818 {
819 g_return_val_if_fail(SP_IS_GRADIENT(gradient), SP_GRADIENT_SPREAD_PAD);
821 SPGradient const *src = chase_hrefs(gradient, has_spread_set);
822 return ( src
823 ? src->spread
824 : SP_GRADIENT_SPREAD_PAD ); // pad is the default
825 }
827 /**
828 * Clears the gradient's svg:stop children from its repr.
829 */
830 void
831 sp_gradient_repr_clear_vector(SPGradient *gr)
832 {
833 Inkscape::XML::Node *repr = SP_OBJECT_REPR(gr);
835 /* Collect stops from original repr */
836 GSList *sl = NULL;
837 for (Inkscape::XML::Node *child = repr->firstChild() ; child != NULL; child = child->next() ) {
838 if (!strcmp(child->name(), "svg:stop")) {
839 sl = g_slist_prepend(sl, child);
840 }
841 }
842 /* Remove all stops */
843 while (sl) {
844 /** \todo
845 * fixme: This should work, unless we make gradient
846 * into generic group.
847 */
848 sp_repr_unparent((Inkscape::XML::Node *)sl->data);
849 sl = g_slist_remove(sl, sl->data);
850 }
851 }
853 /**
854 * Writes the gradient's internal vector (whether from its own stops, or
855 * inherited from refs) into the gradient repr as svg:stop elements.
856 */
857 void
858 sp_gradient_repr_write_vector(SPGradient *gr)
859 {
860 g_return_if_fail(gr != NULL);
861 g_return_if_fail(SP_IS_GRADIENT(gr));
863 Inkscape::XML::Document *xml_doc = sp_document_repr_doc(SP_OBJECT_DOCUMENT(gr));
864 Inkscape::XML::Node *repr = SP_OBJECT_REPR(gr);
866 /* We have to be careful, as vector may be our own, so construct repr list at first */
867 GSList *cl = NULL;
869 for (guint i = 0; i < gr->vector.stops.size(); i++) {
870 Inkscape::CSSOStringStream os;
871 Inkscape::XML::Node *child = xml_doc->createElement("svg:stop");
872 sp_repr_set_css_double(child, "offset", gr->vector.stops[i].offset);
873 /* strictly speaking, offset an SVG <number> rather than a CSS one, but exponents make no
874 * sense for offset proportions. */
875 gchar c[64];
876 sp_svg_write_color(c, 64, sp_color_get_rgba32_ualpha(&gr->vector.stops[i].color, 0x00));
877 os << "stop-color:" << c << ";stop-opacity:" << gr->vector.stops[i].opacity;
878 child->setAttribute("style", os.str().c_str());
879 /* Order will be reversed here */
880 cl = g_slist_prepend(cl, child);
881 }
883 sp_gradient_repr_clear_vector(gr);
885 /* And insert new children from list */
886 while (cl) {
887 Inkscape::XML::Node *child = static_cast<Inkscape::XML::Node *>(cl->data);
888 repr->addChild(child, NULL);
889 Inkscape::GC::release(child);
890 cl = g_slist_remove(cl, child);
891 }
892 }
895 static void
896 gradient_ref_modified(SPObject *href, guint flags, SPGradient *gradient)
897 {
898 if (sp_gradient_invalidate_vector(gradient)) {
899 SP_OBJECT(gradient)->requestModified(SP_OBJECT_MODIFIED_FLAG);
900 /* Conditional to avoid causing infinite loop if there's a cycle in the href chain. */
901 }
902 }
904 /** Return true iff change made. */
905 static bool
906 sp_gradient_invalidate_vector(SPGradient *gr)
907 {
908 bool ret = false;
910 if (gr->color != NULL) {
911 g_free(gr->color);
912 gr->color = NULL;
913 ret = true;
914 }
916 if (gr->vector.built) {
917 gr->vector.built = false;
918 gr->vector.stops.clear();
919 ret = true;
920 }
922 return ret;
923 }
925 /** Creates normalized color vector */
926 static void
927 sp_gradient_rebuild_vector(SPGradient *gr)
928 {
929 gint len = 0;
930 for ( SPObject *child = sp_object_first_child(SP_OBJECT(gr)) ;
931 child != NULL ;
932 child = SP_OBJECT_NEXT(child) ) {
933 if (SP_IS_STOP(child)) {
934 len ++;
935 }
936 }
938 gr->has_stops = (len != 0);
940 gr->vector.stops.clear();
942 SPGradient *ref = gr->ref->getObject();
943 if ( !gr->has_stops && ref ) {
944 /* Copy vector from referenced gradient */
945 gr->vector.built = true; // Prevent infinite recursion.
946 sp_gradient_ensure_vector(ref);
947 if (!ref->vector.stops.empty()) {
948 gr->vector.built = ref->vector.built;
949 gr->vector.stops.assign(ref->vector.stops.begin(), ref->vector.stops.end());
950 return;
951 }
952 }
954 for (SPObject *child = sp_object_first_child(SP_OBJECT(gr)) ;
955 child != NULL;
956 child = SP_OBJECT_NEXT(child) ) {
957 if (SP_IS_STOP(child)) {
958 SPStop *stop = SP_STOP(child);
960 SPGradientStop gstop;
961 if (gr->vector.stops.size() > 0) {
962 // "Each gradient offset value is required to be equal to or greater than the
963 // previous gradient stop's offset value. If a given gradient stop's offset
964 // value is not equal to or greater than all previous offset values, then the
965 // offset value is adjusted to be equal to the largest of all previous offset
966 // values."
967 gstop.offset = MAX(stop->offset, gr->vector.stops.back().offset);
968 } else {
969 gstop.offset = stop->offset;
970 }
972 // "Gradient offset values less than 0 (or less than 0%) are rounded up to
973 // 0%. Gradient offset values greater than 1 (or greater than 100%) are rounded
974 // down to 100%."
975 gstop.offset = CLAMP(gstop.offset, 0, 1);
977 gstop.color = sp_stop_get_color(stop);
978 gstop.opacity = stop->opacity;
980 gr->vector.stops.push_back(gstop);
981 }
982 }
984 // Normalize per section 13.2.4 of SVG 1.1.
985 if (gr->vector.stops.size() == 0) {
986 /* "If no stops are defined, then painting shall occur as if 'none' were specified as the
987 * paint style."
988 */
989 {
990 SPGradientStop gstop;
991 gstop.offset = 0.0;
992 sp_color_set_rgb_rgba32(&gstop.color, 0x00000000);
993 gstop.opacity = 0.0;
994 gr->vector.stops.push_back(gstop);
995 }
996 {
997 SPGradientStop gstop;
998 gstop.offset = 1.0;
999 sp_color_set_rgb_rgba32(&gstop.color, 0x00000000);
1000 gstop.opacity = 0.0;
1001 gr->vector.stops.push_back(gstop);
1002 }
1003 } else {
1004 /* "If one stop is defined, then paint with the solid color fill using the color defined
1005 * for that gradient stop."
1006 */
1007 if (gr->vector.stops.front().offset > 0.0) {
1008 // If the first one is not at 0, then insert a copy of the first at 0.
1009 SPGradientStop gstop;
1010 gstop.offset = 0.0;
1011 sp_color_copy(&gstop.color, &gr->vector.stops.front().color);
1012 gstop.opacity = gr->vector.stops.front().opacity;
1013 gr->vector.stops.insert(gr->vector.stops.begin(), gstop);
1014 }
1015 if (gr->vector.stops.back().offset < 1.0) {
1016 // If the last one is not at 1, then insert a copy of the last at 1.
1017 SPGradientStop gstop;
1018 gstop.offset = 1.0;
1019 sp_color_copy(&gstop.color, &gr->vector.stops.back().color);
1020 gstop.opacity = gr->vector.stops.back().opacity;
1021 gr->vector.stops.push_back(gstop);
1022 }
1023 }
1025 gr->vector.built = true;
1026 }
1028 /**
1029 * The gradient's color array is newly created and set up from vector.
1030 */
1031 void
1032 sp_gradient_ensure_colors(SPGradient *gr)
1033 {
1034 if (!gr->vector.built) {
1035 sp_gradient_rebuild_vector(gr);
1036 }
1037 g_return_if_fail(!gr->vector.stops.empty());
1039 /// \todo Where is the memory freed?
1040 if (!gr->color) {
1041 gr->color = g_new(guchar, 4 * NCOLORS);
1042 }
1044 for (guint i = 0; i < gr->vector.stops.size() - 1; i++) {
1045 guint32 color = sp_color_get_rgba32_falpha(&gr->vector.stops[i].color,
1046 gr->vector.stops[i].opacity);
1047 gint r0 = (color >> 24) & 0xff;
1048 gint g0 = (color >> 16) & 0xff;
1049 gint b0 = (color >> 8) & 0xff;
1050 gint a0 = color & 0xff;
1051 color = sp_color_get_rgba32_falpha(&gr->vector.stops[i + 1].color,
1052 gr->vector.stops[i + 1].opacity);
1053 gint r1 = (color >> 24) & 0xff;
1054 gint g1 = (color >> 16) & 0xff;
1055 gint b1 = (color >> 8) & 0xff;
1056 gint a1 = color & 0xff;
1057 gint o0 = (gint) floor(gr->vector.stops[i].offset * (NCOLORS - 0.001));
1058 gint o1 = (gint) floor(gr->vector.stops[i + 1].offset * (NCOLORS - 0.001));
1059 if (o1 > o0) {
1060 gint dr = ((r1 - r0) << 16) / (o1 - o0);
1061 gint dg = ((g1 - g0) << 16) / (o1 - o0);
1062 gint db = ((b1 - b0) << 16) / (o1 - o0);
1063 gint da = ((a1 - a0) << 16) / (o1 - o0);
1064 gint r = r0 << 16;
1065 gint g = g0 << 16;
1066 gint b = b0 << 16;
1067 gint a = a0 << 16;
1068 for (int j = o0; j < o1 + 1; j++) {
1069 gr->color[4 * j] = r >> 16;
1070 gr->color[4 * j + 1] = g >> 16;
1071 gr->color[4 * j + 2] = b >> 16;
1072 gr->color[4 * j + 3] = a >> 16;
1073 r += dr;
1074 g += dg;
1075 b += db;
1076 a += da;
1077 }
1078 }
1079 }
1080 }
1082 /**
1083 * Renders gradient vector to buffer as line.
1084 *
1085 * RGB buffer background should be set up beforehand.
1086 *
1087 * @param len,width,height,rowstride Buffer parameters (1 or 2 dimensional).
1088 * @param span Full integer width of requested gradient.
1089 * @param pos Buffer starting position in span.
1090 */
1091 static void
1092 sp_gradient_render_vector_line_rgba(SPGradient *const gradient, guchar *buf,
1093 gint const len, gint const pos, gint const span)
1094 {
1095 g_return_if_fail(gradient != NULL);
1096 g_return_if_fail(SP_IS_GRADIENT(gradient));
1097 g_return_if_fail(buf != NULL);
1098 g_return_if_fail(len > 0);
1099 g_return_if_fail(pos >= 0);
1100 g_return_if_fail(pos + len <= span);
1101 g_return_if_fail(span > 0);
1103 if (!gradient->color) {
1104 sp_gradient_ensure_colors(gradient);
1105 }
1107 gint idx = (pos * 1024 << 8) / span;
1108 gint didx = (1024 << 8) / span;
1110 for (gint x = 0; x < len; x++) {
1111 /// \todo Can this be done with 4 byte copies?
1112 *buf++ = gradient->color[4 * (idx >> 8)];
1113 *buf++ = gradient->color[4 * (idx >> 8) + 1];
1114 *buf++ = gradient->color[4 * (idx >> 8) + 2];
1115 *buf++ = gradient->color[4 * (idx >> 8) + 3];
1116 idx += didx;
1117 }
1118 }
1120 /**
1121 * Render rectangular RGBA area from gradient vector.
1122 */
1123 void
1124 sp_gradient_render_vector_block_rgba(SPGradient *const gradient, guchar *buf,
1125 gint const width, gint const height, gint const rowstride,
1126 gint const pos, gint const span, bool const horizontal)
1127 {
1128 g_return_if_fail(gradient != NULL);
1129 g_return_if_fail(SP_IS_GRADIENT(gradient));
1130 g_return_if_fail(buf != NULL);
1131 g_return_if_fail(width > 0);
1132 g_return_if_fail(height > 0);
1133 g_return_if_fail(pos >= 0);
1134 g_return_if_fail((horizontal && (pos + width <= span)) || (!horizontal && (pos + height <= span)));
1135 g_return_if_fail(span > 0);
1137 if (horizontal) {
1138 sp_gradient_render_vector_line_rgba(gradient, buf, width, pos, span);
1139 for (gint y = 1; y < height; y++) {
1140 memcpy(buf + y * rowstride, buf, 4 * width);
1141 }
1142 } else {
1143 guchar *tmp = (guchar *)alloca(4 * height);
1144 sp_gradient_render_vector_line_rgba(gradient, tmp, height, pos, span);
1145 for (gint y = 0; y < height; y++) {
1146 guchar *b = buf + y * rowstride;
1147 for (gint x = 0; x < width; x++) {
1148 *b++ = tmp[0];
1149 *b++ = tmp[1];
1150 *b++ = tmp[2];
1151 *b++ = tmp[3];
1152 }
1153 tmp += 4;
1154 }
1155 }
1156 }
1158 /**
1159 * Render rectangular RGB area from gradient vector.
1160 */
1161 void
1162 sp_gradient_render_vector_block_rgb(SPGradient *gradient, guchar *buf,
1163 gint const width, gint const height, gint const rowstride,
1164 gint const pos, gint const span, bool const horizontal)
1165 {
1166 g_return_if_fail(gradient != NULL);
1167 g_return_if_fail(SP_IS_GRADIENT(gradient));
1168 g_return_if_fail(buf != NULL);
1169 g_return_if_fail(width > 0);
1170 g_return_if_fail(height > 0);
1171 g_return_if_fail(pos >= 0);
1172 g_return_if_fail((horizontal && (pos + width <= span)) || (!horizontal && (pos + height <= span)));
1173 g_return_if_fail(span > 0);
1175 if (horizontal) {
1176 guchar *tmp = (guchar*)alloca(4 * width);
1177 sp_gradient_render_vector_line_rgba(gradient, tmp, width, pos, span);
1178 for (gint y = 0; y < height; y++) {
1179 guchar *t = tmp;
1180 for (gint x = 0; x < width; x++) {
1181 gint a = t[3];
1182 gint fc = (t[0] - buf[0]) * a;
1183 buf[0] = buf[0] + ((fc + (fc >> 8) + 0x80) >> 8);
1184 fc = (t[1] - buf[1]) * a;
1185 buf[1] = buf[1] + ((fc + (fc >> 8) + 0x80) >> 8);
1186 fc = (t[2] - buf[2]) * a;
1187 buf[2] = buf[2] + ((fc + (fc >> 8) + 0x80) >> 8);
1188 buf += 3;
1189 t += 4;
1190 }
1191 }
1192 } else {
1193 guchar *tmp = (guchar*)alloca(4 * height);
1194 sp_gradient_render_vector_line_rgba(gradient, tmp, height, pos, span);
1195 for (gint y = 0; y < height; y++) {
1196 guchar *t = tmp + 4 * y;
1197 for (gint x = 0; x < width; x++) {
1198 gint a = t[3];
1199 gint fc = (t[0] - buf[0]) * a;
1200 buf[0] = buf[0] + ((fc + (fc >> 8) + 0x80) >> 8);
1201 fc = (t[1] - buf[1]) * a;
1202 buf[1] = buf[1] + ((fc + (fc >> 8) + 0x80) >> 8);
1203 fc = (t[2] - buf[2]) * a;
1204 buf[2] = buf[2] + ((fc + (fc >> 8) + 0x80) >> 8);
1205 }
1206 }
1207 }
1208 }
1210 NR::Matrix
1211 sp_gradient_get_g2d_matrix(SPGradient const *gr, NR::Matrix const &ctm, NR::Rect const &bbox)
1212 {
1213 if (gr->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) {
1214 return ( NR::scale(bbox.dimensions())
1215 * NR::translate(bbox.min())
1216 * ctm );
1217 } else {
1218 return ctm;
1219 }
1220 }
1222 NR::Matrix
1223 sp_gradient_get_gs2d_matrix(SPGradient const *gr, NR::Matrix const &ctm, NR::Rect const &bbox)
1224 {
1225 if (gr->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) {
1226 return ( gr->gradientTransform
1227 * NR::scale(bbox.dimensions())
1228 * NR::translate(bbox.min())
1229 * ctm );
1230 } else {
1231 return gr->gradientTransform * ctm;
1232 }
1233 }
1235 void
1236 sp_gradient_set_gs2d_matrix(SPGradient *gr, NR::Matrix const &ctm,
1237 NR::Rect const &bbox, NR::Matrix const &gs2d)
1238 {
1239 gr->gradientTransform = gs2d / ctm;
1240 if ( gr->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX ) {
1241 gr->gradientTransform = ( gr->gradientTransform
1242 / NR::translate(bbox.min())
1243 / NR::scale(bbox.dimensions()) );
1244 }
1245 gr->gradientTransform_set = TRUE;
1247 SP_OBJECT(gr)->requestModified(SP_OBJECT_MODIFIED_FLAG);
1248 }
1250 /*
1251 * Linear Gradient
1252 */
1254 class SPLGPainter;
1256 /// A context with linear gradient, painter, and gradient renderer.
1257 struct SPLGPainter {
1258 SPPainter painter;
1259 SPLinearGradient *lg;
1261 NRLGradientRenderer lgr;
1262 };
1264 static void sp_lineargradient_class_init(SPLinearGradientClass *klass);
1265 static void sp_lineargradient_init(SPLinearGradient *lg);
1267 static void sp_lineargradient_build(SPObject *object,
1268 SPDocument *document,
1269 Inkscape::XML::Node *repr);
1270 static void sp_lineargradient_set(SPObject *object, unsigned key, gchar const *value);
1271 static Inkscape::XML::Node *sp_lineargradient_write(SPObject *object, Inkscape::XML::Node *repr,
1272 guint flags);
1274 static SPPainter *sp_lineargradient_painter_new(SPPaintServer *ps,
1275 NR::Matrix const &full_transform,
1276 NR::Matrix const &parent_transform,
1277 NRRect const *bbox);
1278 static void sp_lineargradient_painter_free(SPPaintServer *ps, SPPainter *painter);
1280 static void sp_lg_fill(SPPainter *painter, NRPixBlock *pb);
1282 static SPGradientClass *lg_parent_class;
1284 /**
1285 * Register SPLinearGradient class and return its type.
1286 */
1287 GType
1288 sp_lineargradient_get_type()
1289 {
1290 static GType type = 0;
1291 if (!type) {
1292 GTypeInfo info = {
1293 sizeof(SPLinearGradientClass),
1294 NULL, NULL,
1295 (GClassInitFunc) sp_lineargradient_class_init,
1296 NULL, NULL,
1297 sizeof(SPLinearGradient),
1298 16,
1299 (GInstanceInitFunc) sp_lineargradient_init,
1300 NULL, /* value_table */
1301 };
1302 type = g_type_register_static(SP_TYPE_GRADIENT, "SPLinearGradient", &info, (GTypeFlags)0);
1303 }
1304 return type;
1305 }
1307 /**
1308 * SPLinearGradient vtable initialization.
1309 */
1310 static void sp_lineargradient_class_init(SPLinearGradientClass *klass)
1311 {
1312 SPObjectClass *sp_object_class = (SPObjectClass *) klass;
1313 SPPaintServerClass *ps_class = (SPPaintServerClass *) klass;
1315 lg_parent_class = (SPGradientClass*)g_type_class_ref(SP_TYPE_GRADIENT);
1317 sp_object_class->build = sp_lineargradient_build;
1318 sp_object_class->set = sp_lineargradient_set;
1319 sp_object_class->write = sp_lineargradient_write;
1321 ps_class->painter_new = sp_lineargradient_painter_new;
1322 ps_class->painter_free = sp_lineargradient_painter_free;
1323 }
1325 /**
1326 * Callback for SPLinearGradient object initialization.
1327 */
1328 static void sp_lineargradient_init(SPLinearGradient *lg)
1329 {
1330 lg->x1.unset(SVGLength::PERCENT, 0.0, 0.0);
1331 lg->y1.unset(SVGLength::PERCENT, 0.5, 0.5);
1332 lg->x2.unset(SVGLength::PERCENT, 1.0, 1.0);
1333 lg->y2.unset(SVGLength::PERCENT, 0.5, 0.5);
1334 }
1336 /**
1337 * Callback: set attributes from associated repr.
1338 */
1339 static void sp_lineargradient_build(SPObject *object,
1340 SPDocument *document,
1341 Inkscape::XML::Node *repr)
1342 {
1343 if (((SPObjectClass *) lg_parent_class)->build)
1344 (* ((SPObjectClass *) lg_parent_class)->build)(object, document, repr);
1346 sp_object_read_attr(object, "x1");
1347 sp_object_read_attr(object, "y1");
1348 sp_object_read_attr(object, "x2");
1349 sp_object_read_attr(object, "y2");
1350 }
1352 /**
1353 * Callback: set attribute.
1354 */
1355 static void
1356 sp_lineargradient_set(SPObject *object, unsigned key, gchar const *value)
1357 {
1358 SPLinearGradient *lg = SP_LINEARGRADIENT(object);
1360 switch (key) {
1361 case SP_ATTR_X1:
1362 lg->x1.readOrUnset(value, SVGLength::PERCENT, 0.0, 0.0);
1363 object->requestModified(SP_OBJECT_MODIFIED_FLAG);
1364 break;
1365 case SP_ATTR_Y1:
1366 lg->y1.readOrUnset(value, SVGLength::PERCENT, 0.5, 0.5);
1367 object->requestModified(SP_OBJECT_MODIFIED_FLAG);
1368 break;
1369 case SP_ATTR_X2:
1370 lg->x2.readOrUnset(value, SVGLength::PERCENT, 1.0, 1.0);
1371 object->requestModified(SP_OBJECT_MODIFIED_FLAG);
1372 break;
1373 case SP_ATTR_Y2:
1374 lg->y2.readOrUnset(value, SVGLength::PERCENT, 0.5, 0.5);
1375 object->requestModified(SP_OBJECT_MODIFIED_FLAG);
1376 break;
1377 default:
1378 if (((SPObjectClass *) lg_parent_class)->set)
1379 (* ((SPObjectClass *) lg_parent_class)->set)(object, key, value);
1380 break;
1381 }
1382 }
1384 /**
1385 * Callback: write attributes to associated repr.
1386 */
1387 static Inkscape::XML::Node *
1388 sp_lineargradient_write(SPObject *object, Inkscape::XML::Node *repr, guint flags)
1389 {
1390 SPLinearGradient *lg = SP_LINEARGRADIENT(object);
1392 if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
1393 Inkscape::XML::Document *xml_doc = sp_document_repr_doc(SP_OBJECT_DOCUMENT(object));
1394 repr = xml_doc->createElement("svg:linearGradient");
1395 }
1397 if ((flags & SP_OBJECT_WRITE_ALL) || lg->x1._set)
1398 sp_repr_set_svg_double(repr, "x1", lg->x1.computed);
1399 if ((flags & SP_OBJECT_WRITE_ALL) || lg->y1._set)
1400 sp_repr_set_svg_double(repr, "y1", lg->y1.computed);
1401 if ((flags & SP_OBJECT_WRITE_ALL) || lg->x2._set)
1402 sp_repr_set_svg_double(repr, "x2", lg->x2.computed);
1403 if ((flags & SP_OBJECT_WRITE_ALL) || lg->y2._set)
1404 sp_repr_set_svg_double(repr, "y2", lg->y2.computed);
1406 if (((SPObjectClass *) lg_parent_class)->write)
1407 (* ((SPObjectClass *) lg_parent_class)->write)(object, repr, flags);
1409 return repr;
1410 }
1412 /**
1413 * Create linear gradient context.
1414 *
1415 * Basically we have to deal with transformations
1416 *
1417 * 1) color2norm - maps point in (0,NCOLORS) vector to (0,1) vector
1418 * 2) norm2pos - maps (0,1) vector to x1,y1 - x2,y2
1419 * 2) gradientTransform
1420 * 3) bbox2user
1421 * 4) ctm == userspace to pixel grid
1422 *
1423 * See also (*) in sp-pattern about why we may need parent_transform.
1424 *
1425 * \todo (point 1 above) fixme: I do not know how to deal with start > 0
1426 * and end < 1.
1427 */
1428 static SPPainter *
1429 sp_lineargradient_painter_new(SPPaintServer *ps,
1430 NR::Matrix const &full_transform,
1431 NR::Matrix const &parent_transform,
1432 NRRect const *bbox)
1433 {
1434 SPLinearGradient *lg = SP_LINEARGRADIENT(ps);
1435 SPGradient *gr = SP_GRADIENT(ps);
1437 if (!gr->color) sp_gradient_ensure_colors(gr);
1439 SPLGPainter *lgp = g_new(SPLGPainter, 1);
1441 lgp->painter.type = SP_PAINTER_IND;
1442 lgp->painter.fill = sp_lg_fill;
1444 lgp->lg = lg;
1446 /** \todo
1447 * Technically speaking, we map NCOLORS on line [start,end] onto line
1448 * [0,1]. I almost think we should fill color array start and end in
1449 * that case. The alternative would be to leave these just empty garbage
1450 * or something similar. Originally I had 1023.9999 here - not sure
1451 * whether we have really to cut out ceil int (Lauris).
1452 */
1453 NR::Matrix color2norm(NR::identity());
1454 NR::Matrix color2px;
1455 if (gr->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) {
1456 NR::Matrix norm2pos(NR::identity());
1458 /* BBox to user coordinate system */
1459 NR::Matrix bbox2user(bbox->x1 - bbox->x0, 0, 0, bbox->y1 - bbox->y0, bbox->x0, bbox->y0);
1461 NR::Matrix color2pos = color2norm * norm2pos;
1462 NR::Matrix color2tpos = color2pos * gr->gradientTransform;
1463 NR::Matrix color2user = color2tpos * bbox2user;
1464 color2px = color2user * full_transform;
1466 } else {
1467 /* Problem: What to do, if we have mixed lengths and percentages? */
1468 /* Currently we do ignore percentages at all, but that is not good (lauris) */
1470 NR::Matrix norm2pos(NR::identity());
1471 NR::Matrix color2pos = color2norm * norm2pos;
1472 NR::Matrix color2tpos = color2pos * gr->gradientTransform;
1473 color2px = color2tpos * full_transform;
1475 }
1477 NRMatrix v2px;
1478 color2px.copyto(&v2px);
1480 nr_lgradient_renderer_setup(&lgp->lgr, gr->color, sp_gradient_get_spread(gr), &v2px,
1481 lg->x1.computed, lg->y1.computed,
1482 lg->x2.computed, lg->y2.computed);
1484 return (SPPainter *) lgp;
1485 }
1487 static void
1488 sp_lineargradient_painter_free(SPPaintServer *ps, SPPainter *painter)
1489 {
1490 g_free(painter);
1491 }
1493 /**
1494 * Directly set properties of linear gradient and request modified.
1495 */
1496 void
1497 sp_lineargradient_set_position(SPLinearGradient *lg,
1498 gdouble x1, gdouble y1,
1499 gdouble x2, gdouble y2)
1500 {
1501 g_return_if_fail(lg != NULL);
1502 g_return_if_fail(SP_IS_LINEARGRADIENT(lg));
1504 /* fixme: units? (Lauris) */
1505 lg->x1.set(SVGLength::NONE, x1, x1);
1506 lg->y1.set(SVGLength::NONE, y1, y1);
1507 lg->x2.set(SVGLength::NONE, x2, x2);
1508 lg->y2.set(SVGLength::NONE, y2, y2);
1510 SP_OBJECT(lg)->requestModified(SP_OBJECT_MODIFIED_FLAG);
1511 }
1513 /**
1514 * Callback when linear gradient object is rendered.
1515 */
1516 static void
1517 sp_lg_fill(SPPainter *painter, NRPixBlock *pb)
1518 {
1519 SPLGPainter *lgp = (SPLGPainter *) painter;
1521 if (lgp->lg->color == NULL) {
1522 sp_gradient_ensure_colors (lgp->lg);
1523 lgp->lgr.vector = lgp->lg->color;
1524 }
1526 nr_render((NRRenderer *) &lgp->lgr, pb, NULL);
1527 }
1529 /*
1530 * Radial Gradient
1531 */
1533 class SPRGPainter;
1535 /// A context with radial gradient, painter, and gradient renderer.
1536 struct SPRGPainter {
1537 SPPainter painter;
1538 SPRadialGradient *rg;
1539 NRRGradientRenderer rgr;
1540 };
1542 static void sp_radialgradient_class_init(SPRadialGradientClass *klass);
1543 static void sp_radialgradient_init(SPRadialGradient *rg);
1545 static void sp_radialgradient_build(SPObject *object,
1546 SPDocument *document,
1547 Inkscape::XML::Node *repr);
1548 static void sp_radialgradient_set(SPObject *object, unsigned key, gchar const *value);
1549 static Inkscape::XML::Node *sp_radialgradient_write(SPObject *object, Inkscape::XML::Node *repr,
1550 guint flags);
1552 static SPPainter *sp_radialgradient_painter_new(SPPaintServer *ps,
1553 NR::Matrix const &full_transform,
1554 NR::Matrix const &parent_transform,
1555 NRRect const *bbox);
1556 static void sp_radialgradient_painter_free(SPPaintServer *ps, SPPainter *painter);
1558 static void sp_rg_fill(SPPainter *painter, NRPixBlock *pb);
1560 static SPGradientClass *rg_parent_class;
1562 /**
1563 * Register SPRadialGradient class and return its type.
1564 */
1565 GType
1566 sp_radialgradient_get_type()
1567 {
1568 static GType type = 0;
1569 if (!type) {
1570 GTypeInfo info = {
1571 sizeof(SPRadialGradientClass),
1572 NULL, NULL,
1573 (GClassInitFunc) sp_radialgradient_class_init,
1574 NULL, NULL,
1575 sizeof(SPRadialGradient),
1576 16,
1577 (GInstanceInitFunc) sp_radialgradient_init,
1578 NULL, /* value_table */
1579 };
1580 type = g_type_register_static(SP_TYPE_GRADIENT, "SPRadialGradient", &info, (GTypeFlags)0);
1581 }
1582 return type;
1583 }
1585 /**
1586 * SPRadialGradient vtable initialization.
1587 */
1588 static void sp_radialgradient_class_init(SPRadialGradientClass *klass)
1589 {
1590 SPObjectClass *sp_object_class = (SPObjectClass *) klass;
1591 SPPaintServerClass *ps_class = (SPPaintServerClass *) klass;
1593 rg_parent_class = (SPGradientClass*)g_type_class_ref(SP_TYPE_GRADIENT);
1595 sp_object_class->build = sp_radialgradient_build;
1596 sp_object_class->set = sp_radialgradient_set;
1597 sp_object_class->write = sp_radialgradient_write;
1599 ps_class->painter_new = sp_radialgradient_painter_new;
1600 ps_class->painter_free = sp_radialgradient_painter_free;
1601 }
1603 /**
1604 * Callback for SPRadialGradient object initialization.
1605 */
1606 static void
1607 sp_radialgradient_init(SPRadialGradient *rg)
1608 {
1609 rg->cx.unset(SVGLength::PERCENT, 0.5, 0.5);
1610 rg->cy.unset(SVGLength::PERCENT, 0.5, 0.5);
1611 rg->r.unset(SVGLength::PERCENT, 0.5, 0.5);
1612 rg->fx.unset(SVGLength::PERCENT, 0.5, 0.5);
1613 rg->fy.unset(SVGLength::PERCENT, 0.5, 0.5);
1614 }
1616 /**
1617 * Set radial gradient attributes from associated repr.
1618 */
1619 static void
1620 sp_radialgradient_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
1621 {
1622 if (((SPObjectClass *) rg_parent_class)->build)
1623 (* ((SPObjectClass *) rg_parent_class)->build)(object, document, repr);
1625 sp_object_read_attr(object, "cx");
1626 sp_object_read_attr(object, "cy");
1627 sp_object_read_attr(object, "r");
1628 sp_object_read_attr(object, "fx");
1629 sp_object_read_attr(object, "fy");
1630 }
1632 /**
1633 * Set radial gradient attribute.
1634 */
1635 static void
1636 sp_radialgradient_set(SPObject *object, unsigned key, gchar const *value)
1637 {
1638 SPRadialGradient *rg = SP_RADIALGRADIENT(object);
1640 switch (key) {
1641 case SP_ATTR_CX:
1642 if (!rg->cx.read(value)) {
1643 rg->cx.unset(SVGLength::PERCENT, 0.5, 0.5);
1644 }
1645 if (!rg->fx._set) {
1646 rg->fx.value = rg->cx.value;
1647 rg->fx.computed = rg->cx.computed;
1648 }
1649 object->requestModified(SP_OBJECT_MODIFIED_FLAG);
1650 break;
1651 case SP_ATTR_CY:
1652 if (!rg->cy.read(value)) {
1653 rg->cy.unset(SVGLength::PERCENT, 0.5, 0.5);
1654 }
1655 if (!rg->fy._set) {
1656 rg->fy.value = rg->cy.value;
1657 rg->fy.computed = rg->cy.computed;
1658 }
1659 object->requestModified(SP_OBJECT_MODIFIED_FLAG);
1660 break;
1661 case SP_ATTR_R:
1662 if (!rg->r.read(value)) {
1663 rg->r.unset(SVGLength::PERCENT, 0.5, 0.5);
1664 }
1665 object->requestModified(SP_OBJECT_MODIFIED_FLAG);
1666 break;
1667 case SP_ATTR_FX:
1668 if (!rg->fx.read(value)) {
1669 rg->fx.unset(rg->cx.unit, rg->cx.value, rg->cx.computed);
1670 }
1671 object->requestModified(SP_OBJECT_MODIFIED_FLAG);
1672 break;
1673 case SP_ATTR_FY:
1674 if (!rg->fy.read(value)) {
1675 rg->fy.unset(rg->cy.unit, rg->cy.value, rg->cy.computed);
1676 }
1677 object->requestModified(SP_OBJECT_MODIFIED_FLAG);
1678 break;
1679 default:
1680 if (((SPObjectClass *) rg_parent_class)->set)
1681 ((SPObjectClass *) rg_parent_class)->set(object, key, value);
1682 break;
1683 }
1684 }
1686 /**
1687 * Write radial gradient attributes to associated repr.
1688 */
1689 static Inkscape::XML::Node *
1690 sp_radialgradient_write(SPObject *object, Inkscape::XML::Node *repr, guint flags)
1691 {
1692 SPRadialGradient *rg = SP_RADIALGRADIENT(object);
1694 if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
1695 Inkscape::XML::Document *xml_doc = sp_document_repr_doc(SP_OBJECT_DOCUMENT(object));
1696 repr = xml_doc->createElement("svg:radialGradient");
1697 }
1699 if ((flags & SP_OBJECT_WRITE_ALL) || rg->cx._set) sp_repr_set_svg_double(repr, "cx", rg->cx.computed);
1700 if ((flags & SP_OBJECT_WRITE_ALL) || rg->cy._set) sp_repr_set_svg_double(repr, "cy", rg->cy.computed);
1701 if ((flags & SP_OBJECT_WRITE_ALL) || rg->r._set) sp_repr_set_svg_double(repr, "r", rg->r.computed);
1702 if ((flags & SP_OBJECT_WRITE_ALL) || rg->fx._set) sp_repr_set_svg_double(repr, "fx", rg->fx.computed);
1703 if ((flags & SP_OBJECT_WRITE_ALL) || rg->fy._set) sp_repr_set_svg_double(repr, "fy", rg->fy.computed);
1705 if (((SPObjectClass *) rg_parent_class)->write)
1706 (* ((SPObjectClass *) rg_parent_class)->write)(object, repr, flags);
1708 return repr;
1709 }
1711 /**
1712 * Create radial gradient context.
1713 */
1714 static SPPainter *
1715 sp_radialgradient_painter_new(SPPaintServer *ps,
1716 NR::Matrix const &full_transform,
1717 NR::Matrix const &parent_transform,
1718 NRRect const *bbox)
1719 {
1720 SPRadialGradient *rg = SP_RADIALGRADIENT(ps);
1721 SPGradient *gr = SP_GRADIENT(ps);
1723 if (!gr->color) sp_gradient_ensure_colors(gr);
1725 SPRGPainter *rgp = g_new(SPRGPainter, 1);
1727 rgp->painter.type = SP_PAINTER_IND;
1728 rgp->painter.fill = sp_rg_fill;
1730 rgp->rg = rg;
1732 NR::Matrix gs2px;
1734 if (gr->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) {
1735 /** \todo
1736 * fixme: We may try to normalize here too, look at
1737 * linearGradient (Lauris)
1738 */
1740 /* BBox to user coordinate system */
1741 NR::Matrix bbox2user(bbox->x1 - bbox->x0, 0, 0, bbox->y1 - bbox->y0, bbox->x0, bbox->y0);
1743 NR::Matrix gs2user = gr->gradientTransform * bbox2user;
1745 gs2px = gs2user * full_transform;
1746 } else {
1747 /** \todo
1748 * Problem: What to do, if we have mixed lengths and percentages?
1749 * Currently we do ignore percentages at all, but that is not
1750 * good (lauris)
1751 */
1753 gs2px = gr->gradientTransform * full_transform;
1754 }
1756 NRMatrix gs2px_nr;
1757 gs2px.copyto(&gs2px_nr);
1759 nr_rgradient_renderer_setup(&rgp->rgr, gr->color, sp_gradient_get_spread(gr),
1760 &gs2px_nr,
1761 rg->cx.computed, rg->cy.computed,
1762 rg->fx.computed, rg->fy.computed,
1763 rg->r.computed);
1765 return (SPPainter *) rgp;
1766 }
1768 static void
1769 sp_radialgradient_painter_free(SPPaintServer *ps, SPPainter *painter)
1770 {
1771 g_free(painter);
1772 }
1774 /**
1775 * Directly set properties of radial gradient and request modified.
1776 */
1777 void
1778 sp_radialgradient_set_position(SPRadialGradient *rg,
1779 gdouble cx, gdouble cy, gdouble fx, gdouble fy, gdouble r)
1780 {
1781 g_return_if_fail(rg != NULL);
1782 g_return_if_fail(SP_IS_RADIALGRADIENT(rg));
1784 /* fixme: units? (Lauris) */
1785 rg->cx.set(SVGLength::NONE, cx, cx);
1786 rg->cy.set(SVGLength::NONE, cy, cy);
1787 rg->fx.set(SVGLength::NONE, fx, fx);
1788 rg->fy.set(SVGLength::NONE, fy, fy);
1789 rg->r.set(SVGLength::NONE, r, r);
1791 SP_OBJECT(rg)->requestModified(SP_OBJECT_MODIFIED_FLAG);
1792 }
1794 /**
1795 * Callback when radial gradient object is rendered.
1796 */
1797 static void
1798 sp_rg_fill(SPPainter *painter, NRPixBlock *pb)
1799 {
1800 SPRGPainter *rgp = (SPRGPainter *) painter;
1802 if (rgp->rg->color == NULL) {
1803 sp_gradient_ensure_colors (rgp->rg);
1804 rgp->rgr.vector = rgp->rg->color;
1805 }
1807 nr_render((NRRenderer *) &rgp->rgr, pb, NULL);
1808 }
1810 /*
1811 Local Variables:
1812 mode:c++
1813 c-file-style:"stroustrup"
1814 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1815 indent-tabs-mode:nil
1816 fill-column:99
1817 End:
1818 */
1819 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :