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