1 /*
2 svg_transform.c: Data structure for SVG transformation matrix.
3
4 Copyright � 2002 USC/Information Sciences Institute
5
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Library General Public License as
8 published by the Free Software Foundation; either version 2 of the
9 License, or (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Library General Public License for more details.
15
16 You should have received a copy of the GNU Library General Public
17 License along with this program; if not, write to the
18 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 Boston, MA 02111-1307, USA.
20
21 Author: Carl Worth <cworth@isi.edu>
22 */
23
24 #include <math.h>
25 #include <string.h>
26
27 #include "svgint.h"
28
29 static svg_status_t
30 _svg_transform_multiply (svg_transform_t *result,
31 const svg_transform_t *t1,
32 const svg_transform_t *t2);
33
34 static svg_transform_t SVG_TRANSFORM_IDENTITY = {
35 {
36 {1, 0},
37 {0, 1},
38 {0, 0}
39 }
40 };
41
42 svg_status_t
_svg_transform_init(svg_transform_t * transform)43 _svg_transform_init (svg_transform_t *transform)
44 {
45 *transform = SVG_TRANSFORM_IDENTITY;
46
47 return SVG_STATUS_SUCCESS;
48 }
49
50 svg_status_t
_svg_transform_init_matrix(svg_transform_t * transform,double a,double b,double c,double d,double e,double f)51 _svg_transform_init_matrix (svg_transform_t *transform,
52 double a, double b,
53 double c, double d,
54 double e, double f)
55 {
56 transform->m[0][0] = a; transform->m[0][1] = b;
57 transform->m[1][0] = c; transform->m[1][1] = d;
58 transform->m[2][0] = e; transform->m[2][1] = f;
59
60 return SVG_STATUS_SUCCESS;
61 }
62
63 svg_status_t
_svg_transform_init_translate(svg_transform_t * transform,double tx,double ty)64 _svg_transform_init_translate (svg_transform_t *transform,
65 double tx, double ty)
66 {
67 return _svg_transform_init_matrix (transform,
68 1, 0,
69 0, 1,
70 tx, ty);
71 }
72
73 svg_status_t
_svg_transform_init_scale(svg_transform_t * transform,double sx,double sy)74 _svg_transform_init_scale (svg_transform_t *transform,
75 double sx, double sy)
76 {
77 return _svg_transform_init_matrix (transform,
78 sx, 0,
79 0, sy,
80 0, 0);
81 }
82
83 /* silly SVG spec. uses degrees */
84 svg_status_t
_svg_transform_init_rotate(svg_transform_t * transform,double angle_degrees)85 _svg_transform_init_rotate (svg_transform_t *transform,
86 double angle_degrees)
87 {
88 double rad = M_PI * angle_degrees / 180.0;
89 return _svg_transform_init_matrix (transform,
90 cos(rad), sin(rad),
91 -sin(rad), cos(rad),
92 0, 0);
93 }
94
95 /* silly SVG spec. uses degrees */
96 svg_status_t
_svg_transform_init_skew_x(svg_transform_t * transform,double angle_degrees)97 _svg_transform_init_skew_x (svg_transform_t *transform,
98 double angle_degrees)
99 {
100 double rad = M_PI * angle_degrees / 180.0;
101 return _svg_transform_init_matrix (transform,
102 1, 0,
103 tan(rad), 1,
104 0, 0);
105 }
106
107 /* silly SVG spec. uses degrees */
108 svg_status_t
_svg_transform_init_skew_y(svg_transform_t * transform,double angle_degrees)109 _svg_transform_init_skew_y (svg_transform_t *transform,
110 double angle_degrees)
111 {
112 double rad = M_PI * angle_degrees / 180.0;
113 return _svg_transform_init_matrix (transform,
114 1, tan(rad),
115 0, 1,
116 0, 0);
117 }
118
119 svg_status_t
_svg_transform_deinit(svg_transform_t * transform)120 _svg_transform_deinit (svg_transform_t *transform)
121 {
122 return _svg_transform_init (transform);
123 }
124
125 svg_status_t
_svg_transform_add_translate(svg_transform_t * transform,double tx,double ty)126 _svg_transform_add_translate (svg_transform_t *transform,
127 double tx, double ty)
128 {
129 svg_transform_t translate;
130
131 _svg_transform_init_translate (&translate, tx, ty);
132 return _svg_transform_multiply_into_right (&translate, transform);
133 }
134
135 svg_status_t
_svg_transform_add_scale_uniform(svg_transform_t * transform,double s)136 _svg_transform_add_scale_uniform (svg_transform_t *transform, double s)
137 {
138 return _svg_transform_add_scale (transform, s, s);
139 }
140
141 svg_status_t
_svg_transform_add_scale(svg_transform_t * transform,double sx,double sy)142 _svg_transform_add_scale (svg_transform_t *transform,
143 double sx, double sy)
144 {
145 svg_transform_t scale;
146
147 _svg_transform_init_scale (&scale, sx, sy);
148 return _svg_transform_multiply_into_right (&scale, transform);
149 }
150
151 /* silly SVG spec. uses degrees */
152 svg_status_t
_svg_transform_add_rotate(svg_transform_t * transform,double angle_degrees)153 _svg_transform_add_rotate (svg_transform_t *transform,
154 double angle_degrees)
155 {
156 svg_transform_t rotate;
157
158 _svg_transform_init_rotate (&rotate, angle_degrees);
159 return _svg_transform_multiply_into_right (&rotate, transform);
160 }
161
162 /* silly SVG spec. uses degrees */
163 svg_status_t
_svg_transform_add_skew_x(svg_transform_t * transform,double angle_degrees)164 _svg_transform_add_skew_x (svg_transform_t *transform,
165 double angle_degrees)
166 {
167 svg_transform_t skew;
168
169 _svg_transform_init_skew_x (&skew, angle_degrees);
170 return _svg_transform_multiply_into_right (&skew, transform);
171 }
172
173 /* silly SVG spec. uses degrees */
174 svg_status_t
_svg_transform_add_skew_y(svg_transform_t * transform,double angle_degrees)175 _svg_transform_add_skew_y (svg_transform_t *transform,
176 double angle_degrees)
177 {
178 svg_transform_t skew;
179
180 _svg_transform_init_skew_y (&skew, angle_degrees);
181 return _svg_transform_multiply_into_right (&skew, transform);
182 }
183
184
185 svg_status_t
_svg_transform_multiply_into_left(svg_transform_t * t1,const svg_transform_t * t2)186 _svg_transform_multiply_into_left (svg_transform_t *t1, const svg_transform_t *t2)
187 {
188 svg_status_t status;
189 svg_transform_t t1_new;
190
191 status = _svg_transform_multiply (&t1_new, t1, t2);
192
193 *t1 = t1_new;
194
195 return status;
196 }
197
198 svg_status_t
_svg_transform_multiply_into_right(const svg_transform_t * t1,svg_transform_t * t2)199 _svg_transform_multiply_into_right (const svg_transform_t *t1, svg_transform_t *t2)
200 {
201 svg_status_t status;
202 svg_transform_t t2_new;
203
204 status = _svg_transform_multiply (&t2_new, t1, t2);
205
206 *t2 = t2_new;
207
208 return status;
209 }
210
211 static svg_status_t
_svg_transform_multiply(svg_transform_t * result,const svg_transform_t * t1,const svg_transform_t * t2)212 _svg_transform_multiply (svg_transform_t *result,
213 const svg_transform_t *t1,
214 const svg_transform_t *t2)
215 {
216 int row, col, n;
217 double t;
218
219 for (row = 0; row < 3; row++) {
220 for (col = 0; col < 2; col++) {
221 if (row == 2)
222 t = t2->m[2][col];
223 else
224 t = 0;
225 for (n = 0; n < 2; n++) {
226 t += t1->m[row][n] * t2->m[n][col];
227 }
228 result->m[row][col] = t;
229 }
230 }
231
232 return SVG_STATUS_SUCCESS;
233 }
234
235 svg_status_t
_svg_transform_render(svg_transform_t * transform,svg_render_engine_t * engine,void * closure)236 _svg_transform_render (svg_transform_t *transform,
237 svg_render_engine_t *engine,
238 void *closure)
239 {
240 return (engine->transform) (closure,
241 transform->m[0][0], transform->m[0][1],
242 transform->m[1][0], transform->m[1][1],
243 transform->m[2][0], transform->m[2][1]);
244 }
245
246
247 /* The following parse function is:
248
249 Copyright (C) 2000 Eazel, Inc.
250 Copyright (C) 2002 Dom Lachowicz <cinamod@hotmail.com>
251
252 Author: Raph Levien <raph@artofcode.com>
253
254 Parse an SVG transform string into an affine matrix. Reference: SVG
255 working draft dated 1999-07-06, section 8.5.
256 */
257 extern svg_status_t
_svg_transform_parse_str(svg_transform_t * transform,const char * str)258 _svg_transform_parse_str (svg_transform_t *transform, const char *str)
259 {
260 int idx;
261 svg_status_t status;
262 char keyword[32];
263 double args[6];
264 int n_args;
265 unsigned int key_len;
266 svg_transform_t tmp_transform;
267
268 status = _svg_transform_init (transform);
269 if (status)
270 return status;
271
272 idx = 0;
273 while (str[idx]) {
274 /* skip initial whitespace */
275 while (_svg_ascii_isspace (str[idx]) || str[idx] == ',')
276 idx++;
277
278 /* parse keyword */
279 for (key_len = 0; key_len < sizeof (keyword); key_len++) {
280 char c;
281
282 c = str[idx];
283 if (_svg_ascii_isalpha (c) || c == '-')
284 keyword[key_len] = str[idx++];
285 else
286 break;
287 }
288 /* XXX: This size limitation looks bogus */
289 if (key_len >= sizeof (keyword))
290 return SVG_STATUS_PARSE_ERROR;
291 keyword[key_len] = '\0';
292
293 /* skip whitespace */
294 while (_svg_ascii_isspace (str[idx]))
295 idx++;
296
297 if (str[idx] != '(')
298 return SVG_STATUS_PARSE_ERROR;
299 idx++;
300
301 for (n_args = 0; ; n_args++) {
302 char c;
303 const char *end_ptr;
304
305 /* skip whitespace */
306 while (_svg_ascii_isspace (str[idx]))
307 idx++;
308 c = str[idx];
309 if (_svg_ascii_isdigit (c) || c == '+' || c == '-' || c == '.') {
310 if (n_args == SVG_ARRAY_SIZE (args))
311 return SVG_STATUS_PARSE_ERROR;
312 args[n_args] = _svg_ascii_strtod (str + idx, &end_ptr);
313 idx = end_ptr - str;
314
315 while (_svg_ascii_isspace (str[idx]))
316 idx++;
317
318 /* skip optional comma */
319 if (str[idx] == ',')
320 idx++;
321 } else if (c == ')')
322 break;
323 else
324 return SVG_STATUS_PARSE_ERROR;
325 }
326 idx++;
327
328 /* ok, have parsed keyword and args, now modify the transform */
329 if (strcmp (keyword, "matrix") == 0) {
330 if (n_args != 6)
331 return SVG_STATUS_PARSE_ERROR;
332 _svg_transform_init_matrix (&tmp_transform,
333 args[0], args[1],
334 args[2], args[3],
335 args[4], args[5]);
336 _svg_transform_multiply_into_right (&tmp_transform, transform);
337 } else if (strcmp (keyword, "translate") == 0) {
338 if (n_args == 1)
339 args[1] = 0;
340 else if (n_args != 2)
341 return SVG_STATUS_PARSE_ERROR;
342 _svg_transform_add_translate (transform, args[0], args[1]);
343 } else if (strcmp (keyword, "scale") == 0) {
344 if (n_args == 1)
345 args[1] = args[0];
346 else if (n_args != 2)
347 return SVG_STATUS_PARSE_ERROR;
348 _svg_transform_add_scale (transform, args[0], args[1]);
349 } else if (strcmp (keyword, "rotate") == 0) {
350 if (n_args != 1)
351 return SVG_STATUS_PARSE_ERROR;
352 _svg_transform_add_rotate (transform, args[0]);
353 } else if (strcmp (keyword, "skewX") == 0) {
354 if (n_args != 1)
355 return SVG_STATUS_PARSE_ERROR;
356 _svg_transform_add_skew_x (transform, args[0]);
357 } else if (strcmp (keyword, "skewY") == 0) {
358 if (n_args != 1)
359 return SVG_STATUS_PARSE_ERROR;
360 _svg_transform_add_skew_y (transform, args[0]);
361 } else
362 return SVG_STATUS_PARSE_ERROR;
363 }
364
365 return SVG_STATUS_SUCCESS;
366 }
367
368 svg_status_t
_svg_transform_apply_attributes(svg_transform_t * transform,const char ** attributes)369 _svg_transform_apply_attributes (svg_transform_t *transform,
370 const char **attributes)
371 {
372 const char *transform_str;
373
374 _svg_attribute_get_string (attributes, "transform", &transform_str, NULL);
375
376 if (transform_str)
377 return _svg_transform_parse_str (transform, transform_str);
378
379 return SVG_STATUS_SUCCESS;
380 }
381
382