1 /* svg_gradient.c: Data structures for SVG gradients
2 
3    Copyright � 2002 USC/Information Sciences Institute
4 
5    This program is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Library General Public License as
7    published by the Free Software Foundation; either version 2 of the
8    License, or (at your option) any later version.
9 
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Library General Public License for more details.
14 
15    You should have received a copy of the GNU Library General Public
16    License along with this program; if not, write to the
17    Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18    Boston, MA 02111-1307, USA.
19 
20    Author: Steven Kramer
21 */
22 
23 #include "svgint.h"
24 
25 #include <string.h>
26 
27 svg_status_t
_svg_gradient_init(svg_gradient_t * gradient)28 _svg_gradient_init (svg_gradient_t *gradient)
29 {
30     int i;
31     svg_transform_t transform;
32 
33     _svg_gradient_set_type (gradient, SVG_GRADIENT_LINEAR);
34 
35     gradient->units = SVG_GRADIENT_UNITS_BBOX;
36     gradient->spread = SVG_GRADIENT_SPREAD_PAD;
37 
38     _svg_transform_init (&transform);
39     for (i = 0; i < 6 ; i++) {
40 	gradient->transform [i] = transform.m[i/2][i%2];
41     }
42 
43     gradient->stops = NULL;
44     gradient->num_stops = 0;
45     gradient->stops_size = 0;
46 
47     return SVG_STATUS_SUCCESS;
48 }
49 
50 svg_status_t
_svg_gradient_init_copy(svg_gradient_t * gradient,svg_gradient_t * other)51 _svg_gradient_init_copy (svg_gradient_t *gradient,
52 			 svg_gradient_t *other)
53 {
54     *gradient = *other;
55 
56     gradient->stops = malloc (gradient->stops_size * sizeof (svg_gradient_stop_t));
57     if (gradient->stops == NULL)
58 	return SVG_STATUS_NO_MEMORY;
59     memcpy (gradient->stops, other->stops, gradient->num_stops * sizeof (svg_gradient_stop_t));
60 
61     return SVG_STATUS_SUCCESS;
62 }
63 
64 svg_status_t
_svg_gradient_deinit(svg_gradient_t * gradient)65 _svg_gradient_deinit (svg_gradient_t *gradient)
66 {
67     if (gradient->stops) {
68 	free (gradient->stops);
69 	gradient->stops = NULL;
70     }
71     gradient->stops_size = 0;
72     gradient->num_stops = 0;
73 
74     return SVG_STATUS_SUCCESS;
75 }
76 
77 svg_status_t
_svg_gradient_set_type(svg_gradient_t * gradient,svg_gradient_type_t type)78 _svg_gradient_set_type (svg_gradient_t *gradient,
79 			svg_gradient_type_t type)
80 {
81     gradient->type = type;
82 
83     /* XXX: Should check what these defaults should really be. */
84 
85     if (gradient->type == SVG_GRADIENT_LINEAR) {
86 	_svg_length_init_unit (&gradient->u.linear.x1, 0, SVG_LENGTH_UNIT_PCT, SVG_LENGTH_ORIENTATION_HORIZONTAL);
87 	_svg_length_init_unit (&gradient->u.linear.y1, 0, SVG_LENGTH_UNIT_PCT, SVG_LENGTH_ORIENTATION_VERTICAL);
88 	_svg_length_init_unit (&gradient->u.linear.x2, 100, SVG_LENGTH_UNIT_PCT, SVG_LENGTH_ORIENTATION_HORIZONTAL);
89 	_svg_length_init_unit (&gradient->u.linear.y2, 0, SVG_LENGTH_UNIT_PCT, SVG_LENGTH_ORIENTATION_VERTICAL);
90     } else {
91 	_svg_length_init_unit (&gradient->u.radial.cx, 50, SVG_LENGTH_UNIT_PCT, SVG_LENGTH_ORIENTATION_HORIZONTAL);
92 	_svg_length_init_unit (&gradient->u.radial.cy, 50, SVG_LENGTH_UNIT_PCT, SVG_LENGTH_ORIENTATION_VERTICAL);
93 	_svg_length_init_unit (&gradient->u.radial.fx, 50, SVG_LENGTH_UNIT_PCT, SVG_LENGTH_ORIENTATION_HORIZONTAL);
94 	_svg_length_init_unit (&gradient->u.radial.fy, 50, SVG_LENGTH_UNIT_PCT, SVG_LENGTH_ORIENTATION_VERTICAL);
95 	_svg_length_init_unit (&gradient->u.radial.r, 50, SVG_LENGTH_UNIT_PCT, SVG_LENGTH_ORIENTATION_HORIZONTAL);
96     }
97 
98     return SVG_STATUS_SUCCESS;
99 }
100 
101 svg_status_t
_svg_gradient_add_stop(svg_gradient_t * gradient,double offset,svg_color_t * color,double opacity)102 _svg_gradient_add_stop (svg_gradient_t *gradient,
103 			double		offset,
104 			svg_color_t	*color,
105 			double		opacity)
106 {
107     svg_gradient_stop_t *new_stops, *stop;
108 
109     if (gradient->num_stops >= gradient->stops_size) {
110 	int old_size = gradient->stops_size;
111 	if (gradient->stops_size)
112 	    gradient->stops_size *= 2;
113 	else
114 	    gradient->stops_size = 2; /* Any useful gradient has at least 2 */
115 	new_stops = realloc (gradient->stops,
116 			     gradient->stops_size * sizeof (svg_gradient_stop_t));
117 	if (new_stops == NULL) {
118 	    gradient->stops_size = old_size;
119 	    return SVG_STATUS_NO_MEMORY;
120 	}
121 	gradient->stops = new_stops;
122     }
123 
124     stop = &gradient->stops[gradient->num_stops++];
125     stop->offset = offset;
126     stop->color = *color;
127     stop->opacity = opacity;
128 
129     return SVG_STATUS_SUCCESS;
130 }
131 
132 svg_status_t
_svg_gradient_apply_attributes(svg_gradient_t * gradient,svg_t * svg,const char ** attributes)133 _svg_gradient_apply_attributes (svg_gradient_t	*gradient,
134 				svg_t		*svg,
135 				const char	**attributes)
136 {
137     svgint_status_t status;
138     const char *href;
139     int i;
140     svg_transform_t transform;
141     const char *str;
142     svg_gradient_t* prototype = 0;
143 
144     /* SPK: still an incomplete set of attributes */
145     _svg_attribute_get_string (attributes, "xlink:href", &href, 0);
146     if (href) {
147     	svg_element_t *ref = NULL;
148 	_svg_fetch_element_by_id (svg, href + 1, &ref);
149 
150 	if (ref && ref->type == SVG_ELEMENT_TYPE_GRADIENT) {
151 	    svg_gradient_t save_gradient = *gradient;
152 
153 	    prototype = &ref->e.gradient;
154 	    _svg_gradient_init_copy (gradient, prototype);
155 
156 	    if (gradient->type != save_gradient.type) {
157 		gradient->type = save_gradient.type;
158 		gradient->u = save_gradient.u;
159 	    }
160 	}
161     }
162 
163     status = _svg_attribute_get_string (attributes, "gradientUnits", &str, "objectBoundingBox");
164     if (status == SVGINT_STATUS_ATTRIBUTE_NOT_FOUND && prototype) {
165 	gradient->units = prototype->units;
166     } else {
167 	if (strcmp (str, "userSpaceOnUse") == 0)
168 	    gradient->units = SVG_GRADIENT_UNITS_USER;
169 	else if (strcmp (str, "objectBoundingBox") == 0)
170 	    gradient->units = SVG_GRADIENT_UNITS_BBOX;
171 	else
172 	    return SVG_STATUS_INVALID_VALUE;
173     }
174 
175     status = _svg_attribute_get_string (attributes, "gradientTransform", &str, 0);
176     if (str) {
177 	_svg_transform_init (&transform);
178 	_svg_transform_parse_str (&transform, str);
179 	for (i = 0 ; i < 6 ; i++) {
180 	    gradient->transform [i] = transform.m[i/2][i%2];
181 	}
182     } else if (prototype) {
183 	for (i = 0 ; i < 6 ; ++i)
184 	    gradient->transform[i] = prototype->transform[i];
185     }
186 
187     status = _svg_attribute_get_string (attributes, "spreadMethod",
188 					&str, "pad");
189     if (status == SVGINT_STATUS_ATTRIBUTE_NOT_FOUND && prototype) {
190 	gradient->spread = prototype->spread;
191     } else {
192 	if (strcmp (str, "pad") == 0)
193 	    gradient->spread = SVG_GRADIENT_SPREAD_PAD;
194 	else if (strcmp (str, "reflect") == 0)
195 	    gradient->spread = SVG_GRADIENT_SPREAD_REFLECT;
196 	else if (strcmp (str, "repeat") == 0)
197 	    gradient->spread = SVG_GRADIENT_SPREAD_REPEAT;
198 	else
199 	    return SVG_STATUS_INVALID_VALUE;
200     }
201 
202     if (prototype && prototype->type != gradient->type)
203 	prototype = NULL;
204 
205     if (gradient->type == SVG_GRADIENT_LINEAR) {
206 	status = _svg_attribute_get_length (attributes, "x1", &gradient->u.linear.x1, "0%");
207 	if (status == SVGINT_STATUS_ATTRIBUTE_NOT_FOUND && prototype)
208 	    gradient->u.linear.x1 = prototype->u.linear.x1;
209 	status = _svg_attribute_get_length (attributes, "y1", &gradient->u.linear.y1, "0%");
210 	if (status == SVGINT_STATUS_ATTRIBUTE_NOT_FOUND && prototype)
211 	    gradient->u.linear.y1 = prototype->u.linear.y1;
212 	status = _svg_attribute_get_length (attributes, "x2", &gradient->u.linear.x2, "100%");
213 	if (status == SVGINT_STATUS_ATTRIBUTE_NOT_FOUND && prototype)
214 	    gradient->u.linear.x2 = prototype->u.linear.x2;
215 	status = _svg_attribute_get_length (attributes, "y2", &gradient->u.linear.y2, "0%");
216 	if (status == SVGINT_STATUS_ATTRIBUTE_NOT_FOUND && prototype)
217 	    gradient->u.linear.y2 = prototype->u.linear.y2;
218     } else {
219 	status = _svg_attribute_get_length (attributes, "cx", &gradient->u.radial.cx, "50%");
220 	if (status == SVGINT_STATUS_ATTRIBUTE_NOT_FOUND && prototype)
221 	    gradient->u.radial.cx = prototype->u.radial.cx;
222 	status = _svg_attribute_get_length (attributes, "cy", &gradient->u.radial.cy, "50%");
223 	if (status == SVGINT_STATUS_ATTRIBUTE_NOT_FOUND && prototype)
224 	    gradient->u.radial.cy = prototype->u.radial.cy;
225 	status = _svg_attribute_get_length (attributes, "r", &gradient->u.radial.r, "50%");
226 	if (status == SVGINT_STATUS_ATTRIBUTE_NOT_FOUND && prototype)
227 	    gradient->u.radial.r = prototype->u.radial.r;
228 
229 	/* fx and fy default to cx and cy */
230 	status = _svg_attribute_get_length (attributes, "fx", &gradient->u.radial.fx, "50%");
231 	if (status == SVGINT_STATUS_ATTRIBUTE_NOT_FOUND)
232 	    gradient->u.radial.fx = gradient->u.radial.cx;
233 
234 	status = _svg_attribute_get_length (attributes, "fy", &gradient->u.radial.fy, "50%");
235 	if (status == SVGINT_STATUS_ATTRIBUTE_NOT_FOUND)
236 	    gradient->u.radial.fy = gradient->u.radial.cy;
237     }
238 
239     return SVG_STATUS_SUCCESS;
240 }
241