├── LICENSE
├── Makefile
├── README.md
├── XGetopt.c
├── XGetopt.h
├── nanosvg.h
└── svg2gcode.c
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 2, June 1991
3 |
4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
6 | Everyone is permitted to copy and distribute verbatim copies
7 | of this license document, but changing it is not allowed.
8 |
9 | Preamble
10 |
11 | The licenses for most software are designed to take away your
12 | freedom to share and change it. By contrast, the GNU General Public
13 | License is intended to guarantee your freedom to share and change free
14 | software--to make sure the software is free for all its users. This
15 | General Public License applies to most of the Free Software
16 | Foundation's software and to any other program whose authors commit to
17 | using it. (Some other Free Software Foundation software is covered by
18 | the GNU Lesser General Public License instead.) You can apply it to
19 | your programs, too.
20 |
21 | When we speak of free software, we are referring to freedom, not
22 | price. Our General Public Licenses are designed to make sure that you
23 | have the freedom to distribute copies of free software (and charge for
24 | this service if you wish), that you receive source code or can get it
25 | if you want it, that you can change the software or use pieces of it
26 | in new free programs; and that you know you can do these things.
27 |
28 | To protect your rights, we need to make restrictions that forbid
29 | anyone to deny you these rights or to ask you to surrender the rights.
30 | These restrictions translate to certain responsibilities for you if you
31 | distribute copies of the software, or if you modify it.
32 |
33 | For example, if you distribute copies of such a program, whether
34 | gratis or for a fee, you must give the recipients all the rights that
35 | you have. You must make sure that they, too, receive or can get the
36 | source code. And you must show them these terms so they know their
37 | rights.
38 |
39 | We protect your rights with two steps: (1) copyright the software, and
40 | (2) offer you this license which gives you legal permission to copy,
41 | distribute and/or modify the software.
42 |
43 | Also, for each author's protection and ours, we want to make certain
44 | that everyone understands that there is no warranty for this free
45 | software. If the software is modified by someone else and passed on, we
46 | want its recipients to know that what they have is not the original, so
47 | that any problems introduced by others will not reflect on the original
48 | authors' reputations.
49 |
50 | Finally, any free program is threatened constantly by software
51 | patents. We wish to avoid the danger that redistributors of a free
52 | program will individually obtain patent licenses, in effect making the
53 | program proprietary. To prevent this, we have made it clear that any
54 | patent must be licensed for everyone's free use or not licensed at all.
55 |
56 | The precise terms and conditions for copying, distribution and
57 | modification follow.
58 |
59 | GNU GENERAL PUBLIC LICENSE
60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61 |
62 | 0. This License applies to any program or other work which contains
63 | a notice placed by the copyright holder saying it may be distributed
64 | under the terms of this General Public License. The "Program", below,
65 | refers to any such program or work, and a "work based on the Program"
66 | means either the Program or any derivative work under copyright law:
67 | that is to say, a work containing the Program or a portion of it,
68 | either verbatim or with modifications and/or translated into another
69 | language. (Hereinafter, translation is included without limitation in
70 | the term "modification".) Each licensee is addressed as "you".
71 |
72 | Activities other than copying, distribution and modification are not
73 | covered by this License; they are outside its scope. The act of
74 | running the Program is not restricted, and the output from the Program
75 | is covered only if its contents constitute a work based on the
76 | Program (independent of having been made by running the Program).
77 | Whether that is true depends on what the Program does.
78 |
79 | 1. You may copy and distribute verbatim copies of the Program's
80 | source code as you receive it, in any medium, provided that you
81 | conspicuously and appropriately publish on each copy an appropriate
82 | copyright notice and disclaimer of warranty; keep intact all the
83 | notices that refer to this License and to the absence of any warranty;
84 | and give any other recipients of the Program a copy of this License
85 | along with the Program.
86 |
87 | You may charge a fee for the physical act of transferring a copy, and
88 | you may at your option offer warranty protection in exchange for a fee.
89 |
90 | 2. You may modify your copy or copies of the Program or any portion
91 | of it, thus forming a work based on the Program, and copy and
92 | distribute such modifications or work under the terms of Section 1
93 | above, provided that you also meet all of these conditions:
94 |
95 | a) You must cause the modified files to carry prominent notices
96 | stating that you changed the files and the date of any change.
97 |
98 | b) You must cause any work that you distribute or publish, that in
99 | whole or in part contains or is derived from the Program or any
100 | part thereof, to be licensed as a whole at no charge to all third
101 | parties under the terms of this License.
102 |
103 | c) If the modified program normally reads commands interactively
104 | when run, you must cause it, when started running for such
105 | interactive use in the most ordinary way, to print or display an
106 | announcement including an appropriate copyright notice and a
107 | notice that there is no warranty (or else, saying that you provide
108 | a warranty) and that users may redistribute the program under
109 | these conditions, and telling the user how to view a copy of this
110 | License. (Exception: if the Program itself is interactive but
111 | does not normally print such an announcement, your work based on
112 | the Program is not required to print an announcement.)
113 |
114 | These requirements apply to the modified work as a whole. If
115 | identifiable sections of that work are not derived from the Program,
116 | and can be reasonably considered independent and separate works in
117 | themselves, then this License, and its terms, do not apply to those
118 | sections when you distribute them as separate works. But when you
119 | distribute the same sections as part of a whole which is a work based
120 | on the Program, the distribution of the whole must be on the terms of
121 | this License, whose permissions for other licensees extend to the
122 | entire whole, and thus to each and every part regardless of who wrote it.
123 |
124 | Thus, it is not the intent of this section to claim rights or contest
125 | your rights to work written entirely by you; rather, the intent is to
126 | exercise the right to control the distribution of derivative or
127 | collective works based on the Program.
128 |
129 | In addition, mere aggregation of another work not based on the Program
130 | with the Program (or with a work based on the Program) on a volume of
131 | a storage or distribution medium does not bring the other work under
132 | the scope of this License.
133 |
134 | 3. You may copy and distribute the Program (or a work based on it,
135 | under Section 2) in object code or executable form under the terms of
136 | Sections 1 and 2 above provided that you also do one of the following:
137 |
138 | a) Accompany it with the complete corresponding machine-readable
139 | source code, which must be distributed under the terms of Sections
140 | 1 and 2 above on a medium customarily used for software interchange; or,
141 |
142 | b) Accompany it with a written offer, valid for at least three
143 | years, to give any third party, for a charge no more than your
144 | cost of physically performing source distribution, a complete
145 | machine-readable copy of the corresponding source code, to be
146 | distributed under the terms of Sections 1 and 2 above on a medium
147 | customarily used for software interchange; or,
148 |
149 | c) Accompany it with the information you received as to the offer
150 | to distribute corresponding source code. (This alternative is
151 | allowed only for noncommercial distribution and only if you
152 | received the program in object code or executable form with such
153 | an offer, in accord with Subsection b above.)
154 |
155 | The source code for a work means the preferred form of the work for
156 | making modifications to it. For an executable work, complete source
157 | code means all the source code for all modules it contains, plus any
158 | associated interface definition files, plus the scripts used to
159 | control compilation and installation of the executable. However, as a
160 | special exception, the source code distributed need not include
161 | anything that is normally distributed (in either source or binary
162 | form) with the major components (compiler, kernel, and so on) of the
163 | operating system on which the executable runs, unless that component
164 | itself accompanies the executable.
165 |
166 | If distribution of executable or object code is made by offering
167 | access to copy from a designated place, then offering equivalent
168 | access to copy the source code from the same place counts as
169 | distribution of the source code, even though third parties are not
170 | compelled to copy the source along with the object code.
171 |
172 | 4. You may not copy, modify, sublicense, or distribute the Program
173 | except as expressly provided under this License. Any attempt
174 | otherwise to copy, modify, sublicense or distribute the Program is
175 | void, and will automatically terminate your rights under this License.
176 | However, parties who have received copies, or rights, from you under
177 | this License will not have their licenses terminated so long as such
178 | parties remain in full compliance.
179 |
180 | 5. You are not required to accept this License, since you have not
181 | signed it. However, nothing else grants you permission to modify or
182 | distribute the Program or its derivative works. These actions are
183 | prohibited by law if you do not accept this License. Therefore, by
184 | modifying or distributing the Program (or any work based on the
185 | Program), you indicate your acceptance of this License to do so, and
186 | all its terms and conditions for copying, distributing or modifying
187 | the Program or works based on it.
188 |
189 | 6. Each time you redistribute the Program (or any work based on the
190 | Program), the recipient automatically receives a license from the
191 | original licensor to copy, distribute or modify the Program subject to
192 | these terms and conditions. You may not impose any further
193 | restrictions on the recipients' exercise of the rights granted herein.
194 | You are not responsible for enforcing compliance by third parties to
195 | this License.
196 |
197 | 7. If, as a consequence of a court judgment or allegation of patent
198 | infringement or for any other reason (not limited to patent issues),
199 | conditions are imposed on you (whether by court order, agreement or
200 | otherwise) that contradict the conditions of this License, they do not
201 | excuse you from the conditions of this License. If you cannot
202 | distribute so as to satisfy simultaneously your obligations under this
203 | License and any other pertinent obligations, then as a consequence you
204 | may not distribute the Program at all. For example, if a patent
205 | license would not permit royalty-free redistribution of the Program by
206 | all those who receive copies directly or indirectly through you, then
207 | the only way you could satisfy both it and this License would be to
208 | refrain entirely from distribution of the Program.
209 |
210 | If any portion of this section is held invalid or unenforceable under
211 | any particular circumstance, the balance of the section is intended to
212 | apply and the section as a whole is intended to apply in other
213 | circumstances.
214 |
215 | It is not the purpose of this section to induce you to infringe any
216 | patents or other property right claims or to contest validity of any
217 | such claims; this section has the sole purpose of protecting the
218 | integrity of the free software distribution system, which is
219 | implemented by public license practices. Many people have made
220 | generous contributions to the wide range of software distributed
221 | through that system in reliance on consistent application of that
222 | system; it is up to the author/donor to decide if he or she is willing
223 | to distribute software through any other system and a licensee cannot
224 | impose that choice.
225 |
226 | This section is intended to make thoroughly clear what is believed to
227 | be a consequence of the rest of this License.
228 |
229 | 8. If the distribution and/or use of the Program is restricted in
230 | certain countries either by patents or by copyrighted interfaces, the
231 | original copyright holder who places the Program under this License
232 | may add an explicit geographical distribution limitation excluding
233 | those countries, so that distribution is permitted only in or among
234 | countries not thus excluded. In such case, this License incorporates
235 | the limitation as if written in the body of this License.
236 |
237 | 9. The Free Software Foundation may publish revised and/or new versions
238 | of the General Public License from time to time. Such new versions will
239 | be similar in spirit to the present version, but may differ in detail to
240 | address new problems or concerns.
241 |
242 | Each version is given a distinguishing version number. If the Program
243 | specifies a version number of this License which applies to it and "any
244 | later version", you have the option of following the terms and conditions
245 | either of that version or of any later version published by the Free
246 | Software Foundation. If the Program does not specify a version number of
247 | this License, you may choose any version ever published by the Free Software
248 | Foundation.
249 |
250 | 10. If you wish to incorporate parts of the Program into other free
251 | programs whose distribution conditions are different, write to the author
252 | to ask for permission. For software which is copyrighted by the Free
253 | Software Foundation, write to the Free Software Foundation; we sometimes
254 | make exceptions for this. Our decision will be guided by the two goals
255 | of preserving the free status of all derivatives of our free software and
256 | of promoting the sharing and reuse of software generally.
257 |
258 | NO WARRANTY
259 |
260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268 | REPAIR OR CORRECTION.
269 |
270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278 | POSSIBILITY OF SUCH DAMAGES.
279 |
280 | END OF TERMS AND CONDITIONS
281 |
282 | How to Apply These Terms to Your New Programs
283 |
284 | If you develop a new program, and you want it to be of the greatest
285 | possible use to the public, the best way to achieve this is to make it
286 | free software which everyone can redistribute and change under these terms.
287 |
288 | To do so, attach the following notices to the program. It is safest
289 | to attach them to the start of each source file to most effectively
290 | convey the exclusion of warranty; and each file should have at least
291 | the "copyright" line and a pointer to where the full notice is found.
292 |
293 | {description}
294 | Copyright (C) {year} {fullname}
295 |
296 | This program is free software; you can redistribute it and/or modify
297 | it under the terms of the GNU General Public License as published by
298 | the Free Software Foundation; either version 2 of the License, or
299 | (at your option) any later version.
300 |
301 | This program is distributed in the hope that it will be useful,
302 | but WITHOUT ANY WARRANTY; without even the implied warranty of
303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
304 | GNU General Public License for more details.
305 |
306 | You should have received a copy of the GNU General Public License along
307 | with this program; if not, write to the Free Software Foundation, Inc.,
308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
309 |
310 | Also add information on how to contact you by electronic and paper mail.
311 |
312 | If the program is interactive, make it output a short notice like this
313 | when it starts in an interactive mode:
314 |
315 | Gnomovision version 69, Copyright (C) year name of author
316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
317 | This is free software, and you are welcome to redistribute it
318 | under certain conditions; type `show c' for details.
319 |
320 | The hypothetical commands `show w' and `show c' should show the appropriate
321 | parts of the General Public License. Of course, the commands you use may
322 | be called something other than `show w' and `show c'; they could even be
323 | mouse-clicks or menu items--whatever suits your program.
324 |
325 | You should also get your employer (if you work as a programmer) or your
326 | school, if any, to sign a "copyright disclaimer" for the program, if
327 | necessary. Here is a sample; alter the names:
328 |
329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program
330 | `Gnomovision' (which makes passes at compilers) written by James Hacker.
331 |
332 | {signature of Ty Coon}, 1 April 1989
333 | Ty Coon, President of Vice
334 |
335 | This General Public License does not permit incorporating your program into
336 | proprietary programs. If your program is a subroutine library, you may
337 | consider it more useful to permit linking proprietary applications with the
338 | library. If this is what you want to do, use the GNU Lesser General
339 | Public License instead of this License.
340 |
341 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | CC = clang
2 | CFLAGS = -Ofast
3 |
4 | all: svg2gcode
5 |
6 | svg2gcode: svg2gcode.c nanosvg.h
7 | $(CC) -o svg2gcode -g svg2gcode.c -lm
8 |
9 | clean:
10 | rm -fr svg2code *.o
11 |
12 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | svg2gcode
2 | =========
3 |
4 | an svg-file to gcode converter using optimized paths. Written in C and nanosvg.h by Mikko Mononen.
5 | Compiles on OS X, Linux, Windows or maybe on every platform having C-compiler.
6 |
7 | The paths are optimized to minimize unnecessary movements of the cutter or whatever you're using.
8 |
9 | Tested on Linux, OS X and Win7. To change the inits and and other needed g-codes, just change the #defines and compile.
10 | No extra needed libraries.
11 |
12 |
13 |
--------------------------------------------------------------------------------
/XGetopt.c:
--------------------------------------------------------------------------------
1 | // XGetopt.cpp Version 1.1
2 | //
3 | // Author: Hans Dietrich
4 | // hdietrich2@hotmail.com
5 | //
6 | // Modified: David Smith
7 | // dave.s@earthcorp.com
8 | // Moved two char declarations from body of function so
9 | // that it can compile as a C function.
10 | // Thanks so much Hans
11 | //
12 | // This software is released into the public domain.
13 | // You are free to use it in any way you like.
14 | //
15 | // This software is provided "as is" with no expressed
16 | // or implied warranty. I accept no liability for any
17 | // damage or loss of business that this software may cause.
18 | //
19 | ///////////////////////////////////////////////////////////////////////////////
20 |
21 | #include
22 | #include
23 | #include "XGetopt.h"
24 |
25 | ///////////////////////////////////////////////////////////////////////////////
26 | //
27 | // X G e t o p t . c p p
28 | //
29 | //
30 | // NAME
31 | // getopt -- parse command line options
32 | //
33 | // SYNOPSIS
34 | // int getopt(int argc, char *argv[], char *optstring)
35 | //
36 | // extern char *optarg;
37 | // extern int optind;
38 | //
39 | // DESCRIPTION
40 | // The getopt() function parses the command line arguments. Its
41 | // arguments argc and argv are the argument count and array as
42 | // passed into the application on program invocation. In the case
43 | // of Visual C++ programs, argc and argv are available via the
44 | // variables __argc and __argv (double underscores), respectively.
45 | // getopt returns the next option letter in argv that matches a
46 | // letter in optstring.
47 | //
48 | // optstring is a string of recognized option letters; if a letter
49 | // is followed by a colon, the option is expected to have an argument
50 | // that may or may not be separated from it by white space. optarg
51 | // is set to point to the start of the option argument on return from
52 | // getopt.
53 | //
54 | // Option letters may be combined, e.g., "-ab" is equivalent to
55 | // "-a -b". Option letters are case sensitive.
56 | //
57 | // getopt places in the external variable optind the argv index
58 | // of the next argument to be processed. optind is initialized
59 | // to 0 before the first call to getopt.
60 | //
61 | // When all options have been processed (i.e., up to the first
62 | // non-option argument), getopt returns EOF, optarg will point
63 | // to the argument, and optind will be set to the argv index of
64 | // the argument. If there are no non-option arguments, optarg
65 | // will be set to NULL.
66 | //
67 | // The special option "--" may be used to delimit the end of the
68 | // options; EOF will be returned, and "--" (and everything after it)
69 | // will be skipped.
70 | //
71 | // RETURN VALUE
72 | // For option letters contained in the string optstring, getopt
73 | // will return the option letter. getopt returns a question mark (?)
74 | // when it encounters an option letter not included in optstring.
75 | // EOF is returned when processing is finished.
76 | //
77 | // BUGS
78 | // 1) Long options are not supported.
79 | // 2) The GNU double-colon extension is not supported.
80 | // 3) The environment variable POSIXLY_CORRECT is not supported.
81 | // 4) The + syntax is not supported.
82 | // 5) The automatic permutation of arguments is not supported.
83 | // 6) This implementation of getopt() returns EOF if an error is
84 | // encountered, instead of -1 as the latest standard requires.
85 | //
86 | // EXAMPLE
87 | // BOOL CMyApp::ProcessCommandLine(int argc, char *argv[])
88 | // {
89 | // int c;
90 | //
91 | // while ((c = getopt(argc, argv, "aBn:")) != EOF)
92 | // {
93 | // switch (c)
94 | // {
95 | // case 'a':
96 | // TRACE(_T("option a\n"));
97 | // //
98 | // // set some flag here
99 | // //
100 | // break;
101 | //
102 | // case 'B':
103 | // TRACE( _T("option B\n"));
104 | // //
105 | // // set some other flag here
106 | // //
107 | // break;
108 | //
109 | // case 'n':
110 | // TRACE(_T("option n: value=%d\n"), atoi(optarg));
111 | // //
112 | // // do something with value here
113 | // //
114 | // break;
115 | //
116 | // case '?':
117 | // TRACE(_T("ERROR: illegal option %s\n"), argv[optind-1]);
118 | // return FALSE;
119 | // break;
120 | //
121 | // default:
122 | // TRACE(_T("WARNING: no handler for option %c\n"), c);
123 | // return FALSE;
124 | // break;
125 | // }
126 | // }
127 | // //
128 | // // check for non-option args here
129 | // //
130 | // return TRUE;
131 | // }
132 | //
133 | ///////////////////////////////////////////////////////////////////////////////
134 |
135 | char *optarg; // global argument pointer
136 | int optind = 0; // global argv index
137 |
138 | int getopt(int argc, char *argv[], char *optstring)
139 | {
140 | static char *next = NULL;
141 | char c, *cp;
142 | if (optind == 0)
143 | next = NULL;
144 |
145 | optarg = NULL;
146 |
147 | if (next == NULL || *next == '\0')
148 | {
149 | if (optind == 0)
150 | optind++;
151 |
152 | if (optind >= argc || argv[optind][0] != '-' || argv[optind][1] == '\0')
153 | {
154 | optarg = NULL;
155 | if (optind < argc)
156 | optarg = argv[optind];
157 | return EOF;
158 | }
159 |
160 | if (strcmp(argv[optind], "--") == 0)
161 | {
162 | optind++;
163 | optarg = NULL;
164 | if (optind < argc)
165 | optarg = argv[optind];
166 | return EOF;
167 | }
168 |
169 | next = argv[optind]+1;
170 | optind++;
171 | }
172 |
173 | c = *next++;
174 | cp = strchr(optstring, c);
175 |
176 | if (cp == NULL || c == ':')
177 | return '?';
178 |
179 | cp++;
180 | if (*cp == ':')
181 | {
182 | if (*next != '\0')
183 | {
184 | optarg = next;
185 | next = NULL;
186 | }
187 | else if (optind < argc)
188 | {
189 | optarg = argv[optind];
190 | optind++;
191 | }
192 | else
193 | {
194 | return '?';
195 | }
196 | }
197 |
198 | return c;
199 | }
200 |
--------------------------------------------------------------------------------
/XGetopt.h:
--------------------------------------------------------------------------------
1 | // XGetopt.h
2 | //
3 | // Author: Hans Dietrich
4 | // hdietrich2@hotmail.com
5 | //
6 | // This software is released into the public domain.
7 | // You are free to use it in any way you like.
8 | //
9 | // This software is provided "as is" with no expressed
10 | // or implied warranty. I accept no liability for any
11 | // damage or loss of business that this software may cause.
12 | //
13 | ///////////////////////////////////////////////////////////////////////////////
14 |
15 | #ifndef XGETOPT_H
16 | #define XGETOPT_H
17 |
18 | extern int optind, opterr;
19 | extern char *optarg;
20 |
21 | int getopt(int argc, char *argv[], char *optstring);
22 |
23 | #endif //XGETOPT_H
24 |
--------------------------------------------------------------------------------
/nanosvg.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013-14 Mikko Mononen memon@inside.org
3 | *
4 | * This software is provided 'as-is', without any express or implied
5 | * warranty. In no event will the authors be held liable for any damages
6 | * arising from the use of this software.
7 | *
8 | * Permission is granted to anyone to use this software for any purpose,
9 | * including commercial applications, and to alter it and redistribute it
10 | * freely, subject to the following restrictions:
11 | *
12 | * 1. The origin of this software must not be misrepresented; you must not
13 | * claim that you wrote the original software. If you use this software
14 | * in a product, an acknowledgment in the product documentation would be
15 | * appreciated but is not required.
16 | * 2. Altered source versions must be plainly marked as such, and must not be
17 | * misrepresented as being the original software.
18 | * 3. This notice may not be removed or altered from any source distribution.
19 | *
20 | * The SVG parser is based on Anti-Graim Geometry 2.4 SVG example
21 | * Copyright (C) 2002-2004 Maxim Shemanarev (McSeem) (http://www.antigrain.com/)
22 | *
23 | * Arc calculation code based on canvg (https://code.google.com/p/canvg/)
24 | *
25 | * Bounding box calculation based on http://blog.hackers-cafe.net/2009/06/how-to-calculate-bezier-curves-bounding.html
26 | *
27 | */
28 |
29 | #ifndef NANOSVG_H
30 | #define NANOSVG_H
31 |
32 | #ifdef __cplusplus
33 | extern "C" {
34 | #endif
35 |
36 | // NanoSVG is a simple stupid single-header-file SVG parse. The output of the parser is a list of cubic bezier shapes.
37 | //
38 | // The library suits well for anything from rendering scalable icons in your editor application to prototyping a game.
39 | //
40 | // NanoSVG supports a wide range of SVG features, but something may be missing, feel free to create a pull request!
41 | //
42 | // The shapes in the SVG images are transformed by the viewBox and converted to specified units.
43 | // That is, you should get the same looking data as your designed in your favorite app.
44 | //
45 | // NanoSVG can return the paths in few different units. For example if you want to render an image, you may choose
46 | // to get the paths in pixels, or if you are feeding the data into a CNC-cutter, you may want to use millimeters.
47 | //
48 | // The units passed to NanoVG should be one of: 'px', 'pt', 'pc' 'mm', 'cm', or 'in'.
49 | // DPI (dots-per-inch) controls how the unit conversion is done.
50 | //
51 | // If you don't know or care about the units stuff, "px" and 96 should get you going.
52 |
53 |
54 | /* Example Usage:
55 | // Load
56 | SNVGImage* image;
57 | image = nsvgParseFromFile("test.svg", "px", 96);
58 | printf("size: %f x %f\n", image->width, image->height);
59 | // Use...
60 | for (shape = image->shapes; shape != NULL; shape = shape->next) {
61 | for (path = shape->paths; path != NULL; path = path->next) {
62 | for (i = 0; i < path->npts-1; i += 3) {
63 | float* p = &path->pts[i*2];
64 | drawCubicBez(p[0],p[1], p[2],p[3], p[4],p[5], p[6],p[7]);
65 | }
66 | }
67 | }
68 | // Delete
69 | nsvgDelete(image);
70 | */
71 |
72 | #define NSVG_PAINT_NONE 0
73 | #define NSVG_PAINT_COLOR 1
74 | #define NSVG_PAINT_LINEAR_GRADIENT 2
75 | #define NSVG_PAINT_RADIAL_GRADIENT 3
76 |
77 | #define NSVG_SPREAD_PAD 0
78 | #define NSVG_SPREAD_REFLECT 1
79 | #define NSVG_SPREAD_REPEAT 2
80 |
81 | typedef struct NSVGgradientStop {
82 | unsigned int color;
83 | float offset;
84 | } NSVGgradientStop;
85 |
86 | typedef struct NSVGgradient {
87 | float xform[6];
88 | char spread;
89 | float fx, fy;
90 | int nstops;
91 | NSVGgradientStop stops[1];
92 | } NSVGgradient;
93 |
94 | typedef struct NSVGpaint {
95 | char type;
96 | union {
97 | unsigned int color;
98 | NSVGgradient* gradient;
99 | };
100 | } NSVGpaint;
101 |
102 | typedef struct NSVGpath
103 | {
104 | float* pts; // Cubic bezier points: x0,y0, [cpx1,cpx1,cpx2,cpy2,x1,y1], ...
105 | int npts; // Total number of bezier points.
106 | char closed; // Flag indicating if shapes should be treated as closed.
107 | float bounds[4]; // Tight bounding box of the shape [minx,miny,maxx,maxy].
108 | struct NSVGpath* next; // Pointer to next path, or NULL if last element.
109 | } NSVGpath;
110 |
111 | typedef struct NSVGshape
112 | {
113 | NSVGpaint fill; // Fill paint
114 | NSVGpaint stroke; // Stroke paint
115 | float opacity; // Opacity of the shape.
116 | float strokeWidth; // Stroke width (scaled)
117 | float bounds[4]; // Tight bounding box of the shape [minx,miny,maxx,maxy].
118 | NSVGpath* paths; // Linked list of paths in the image.
119 | struct NSVGshape* next; // Pointer to next shape, or NULL if last element.
120 | } NSVGshape;
121 |
122 | typedef struct NSVGimage
123 | {
124 | float width; // Width of the image.
125 | float height; // Height of the image.
126 | NSVGshape* shapes; // Linked list of shapes in the image.
127 | } NSVGimage;
128 |
129 | // Parses SVG file from a file, returns SVG image as paths.
130 | NSVGimage* nsvgParseFromFile(const char* filename, const char* units, float dpi);
131 |
132 | // Parses SVG file from a null terminated string, returns SVG image as paths.
133 | NSVGimage* nsvgParse(char* input, const char* units, float dpi);
134 |
135 | // Deletes list of paths.
136 | void nsvgDelete(NSVGimage* image);
137 |
138 | #ifdef __cplusplus
139 | };
140 | #endif
141 |
142 | #endif // NANOSVG_H
143 |
144 | #ifdef NANOSVG_IMPLEMENTATION
145 |
146 | #include
147 | #include
148 | #include
149 |
150 | #define NSVG_PI (3.14159265358979323846264338327f)
151 | #define NSVG_KAPPA90 (0.5522847493f) // Lenght proportional to radius of a cubic bezier handle for 90deg arcs.
152 |
153 | #define NSVG_ALIGN_MIN 0
154 | #define NSVG_ALIGN_MID 1
155 | #define NSVG_ALIGN_MAX 2
156 | #define NSVG_ALIGN_NONE 0
157 | #define NSVG_ALIGN_MEET 1
158 | #define NSVG_ALIGN_SLICE 2
159 |
160 | #define NSVG_NOTUSED(v) do { (void)(1 ? (void)0 : ( (void)(v) ) ); } while(0)
161 | #define NSVG_RGB(r, g, b) (((unsigned int)r) | ((unsigned int)g << 8) | ((unsigned int)b << 16))
162 |
163 | #ifdef _MSC_VER
164 | #pragma warning (disable: 4996) // Switch off security warnings
165 | #pragma warning (disable: 4100) // Switch off unreferenced formal parameter warnings
166 | #ifdef __cplusplus
167 | #define NSVG_INLINE inline
168 | #else
169 | #define NSVG_INLINE
170 | #endif
171 | #else
172 | #define NSVG_INLINE inline
173 | #endif
174 |
175 |
176 | static int nsvg__isspace(char c)
177 | {
178 | return strchr(" \t\n\v\f\r", c) != 0;
179 | }
180 |
181 | static int nsvg__isdigit(char c)
182 | {
183 | return strchr("0123456789", c) != 0;
184 | }
185 |
186 | static int nsvg__isnum(char c)
187 | {
188 | return strchr("0123456789+-.eE", c) != 0;
189 | }
190 |
191 | static NSVG_INLINE float nsvg__minf(float a, float b) { return a < b ? a : b; }
192 | static NSVG_INLINE float nsvg__maxf(float a, float b) { return a > b ? a : b; }
193 |
194 |
195 | // Simple XML parser
196 |
197 | #define NSVG_XML_TAG 1
198 | #define NSVG_XML_CONTENT 2
199 | #define NSVG_XML_MAX_ATTRIBS 256
200 |
201 | static void nsvg__parseContent(char* s,
202 | void (*contentCb)(void* ud, const char* s),
203 | void* ud)
204 | {
205 | // Trim start white spaces
206 | while (*s && nsvg__isspace(*s)) s++;
207 | if (!*s) return;
208 |
209 | if (contentCb)
210 | (*contentCb)(ud, s);
211 | }
212 |
213 | static void nsvg__parseElement(char* s,
214 | void (*startelCb)(void* ud, const char* el, const char** attr),
215 | void (*endelCb)(void* ud, const char* el),
216 | void* ud)
217 | {
218 | const char* attr[NSVG_XML_MAX_ATTRIBS];
219 | int nattr = 0;
220 | char* name;
221 | int start = 0;
222 | int end = 0;
223 | char quote;
224 |
225 | // Skip white space after the '<'
226 | while (*s && nsvg__isspace(*s)) s++;
227 |
228 | // Check if the tag is end tag
229 | if (*s == '/') {
230 | s++;
231 | end = 1;
232 | } else {
233 | start = 1;
234 | }
235 |
236 | // Skip comments, data and preprocessor stuff.
237 | if (!*s || *s == '?' || *s == '!')
238 | return;
239 |
240 | // Get tag name
241 | name = s;
242 | while (*s && !nsvg__isspace(*s)) s++;
243 | if (*s) { *s++ = '\0'; }
244 |
245 | // Get attribs
246 | while (!end && *s && nattr < NSVG_XML_MAX_ATTRIBS-3) {
247 | // Skip white space before the attrib name
248 | while (*s && nsvg__isspace(*s)) s++;
249 | if (!*s) break;
250 | if (*s == '/') {
251 | end = 1;
252 | break;
253 | }
254 | attr[nattr++] = s;
255 | // Find end of the attrib name.
256 | while (*s && !nsvg__isspace(*s) && *s != '=') s++;
257 | if (*s) { *s++ = '\0'; }
258 | // Skip until the beginning of the value.
259 | while (*s && *s != '\"' && *s != '\'') s++;
260 | if (!*s) break;
261 | quote = *s;
262 | s++;
263 | // Store value and find the end of it.
264 | attr[nattr++] = s;
265 | while (*s && *s != quote) s++;
266 | if (*s) { *s++ = '\0'; }
267 | }
268 |
269 | // List terminator
270 | attr[nattr++] = 0;
271 | attr[nattr++] = 0;
272 |
273 | // Call callbacks.
274 | if (start && startelCb)
275 | (*startelCb)(ud, name, attr);
276 | if (end && endelCb)
277 | (*endelCb)(ud, name);
278 | }
279 |
280 | int nsvg__parseXML(char* input,
281 | void (*startelCb)(void* ud, const char* el, const char** attr),
282 | void (*endelCb)(void* ud, const char* el),
283 | void (*contentCb)(void* ud, const char* s),
284 | void* ud)
285 | {
286 | char* s = input;
287 | char* mark = s;
288 | int state = NSVG_XML_CONTENT;
289 | while (*s) {
290 | if (*s == '<' && state == NSVG_XML_CONTENT) {
291 | // Start of a tag
292 | *s++ = '\0';
293 | nsvg__parseContent(mark, contentCb, ud);
294 | mark = s;
295 | state = NSVG_XML_TAG;
296 | } else if (*s == '>' && state == NSVG_XML_TAG) {
297 | // Start of a content or new tag.
298 | *s++ = '\0';
299 | nsvg__parseElement(mark, startelCb, endelCb, ud);
300 | mark = s;
301 | state = NSVG_XML_CONTENT;
302 | } else {
303 | s++;
304 | }
305 | }
306 |
307 | return 1;
308 | }
309 |
310 |
311 | /* Simple SVG parser. */
312 |
313 | #define NSVG_MAX_ATTR 128
314 |
315 | #define NSVG_USER_SPACE 0
316 | #define NSVG_OBJECT_SPACE 1
317 |
318 | typedef struct NSVGlinearData {
319 | float x1, y1, x2, y2;
320 | } NSVGlinearData;
321 |
322 | typedef struct NSVGradialData {
323 | float cx, cy, r, fx, fy;
324 | } NSVGradialData;
325 |
326 | typedef struct NSVGgradientData
327 | {
328 | char id[64];
329 | char ref[64];
330 | char type;
331 | union {
332 | NSVGlinearData linear;
333 | NSVGradialData radial;
334 | };
335 | char spread;
336 | char units;
337 | float xform[6];
338 | int nstops;
339 | NSVGgradientStop* stops;
340 | struct NSVGgradientData* next;
341 | } NSVGgradientData;
342 |
343 | typedef struct NSVGattrib
344 | {
345 | float xform[6];
346 | unsigned int fillColor;
347 | unsigned int strokeColor;
348 | float opacity;
349 | float fillOpacity;
350 | float strokeOpacity;
351 | char fillGradient[64];
352 | char strokeGradient[64];
353 | float strokeWidth;
354 | float fontSize;
355 | unsigned int stopColor;
356 | float stopOpacity;
357 | float stopOffset;
358 | char hasFill;
359 | char hasStroke;
360 | char visible;
361 | } NSVGattrib;
362 |
363 | typedef struct NSVGparser
364 | {
365 | NSVGattrib attr[NSVG_MAX_ATTR];
366 | int attrHead;
367 | float* pts;
368 | int npts;
369 | int cpts;
370 | NSVGpath* plist;
371 | NSVGimage* image;
372 | NSVGgradientData* gradients;
373 | float viewMinx, viewMiny, viewWidth, viewHeight;
374 | int alignX, alignY, alignType;
375 | float dpi;
376 | char pathFlag;
377 | char defsFlag;
378 | } NSVGparser;
379 |
380 | static void nsvg__xformIdentity(float* t)
381 | {
382 | t[0] = 1.0f; t[1] = 0.0f;
383 | t[2] = 0.0f; t[3] = 1.0f;
384 | t[4] = 0.0f; t[5] = 0.0f;
385 | }
386 |
387 | static void nsvg__xformSetTranslation(float* t, float tx, float ty)
388 | {
389 | t[0] = 1.0f; t[1] = 0.0f;
390 | t[2] = 0.0f; t[3] = 1.0f;
391 | t[4] = tx; t[5] = ty;
392 | }
393 |
394 | static void nsvg__xformSetScale(float* t, float sx, float sy)
395 | {
396 | t[0] = sx; t[1] = 0.0f;
397 | t[2] = 0.0f; t[3] = sy;
398 | t[4] = 0.0f; t[5] = 0.0f;
399 | }
400 |
401 | static void nsvg__xformSetSkewX(float* t, float a)
402 | {
403 | t[0] = 1.0f; t[1] = 0.0f;
404 | t[2] = tanf(a); t[3] = 1.0f;
405 | t[4] = 0.0f; t[5] = 0.0f;
406 | }
407 |
408 | static void nsvg__xformSetSkewY(float* t, float a)
409 | {
410 | t[0] = 1.0f; t[1] = tanf(a);
411 | t[2] = 0.0f; t[3] = 1.0f;
412 | t[4] = 0.0f; t[5] = 0.0f;
413 | }
414 |
415 | static void nsvg__xformSetRotation(float* t, float a)
416 | {
417 | float cs = cosf(a), sn = sinf(a);
418 | t[0] = cs; t[1] = sn;
419 | t[2] = -sn; t[3] = cs;
420 | t[4] = 0.0f; t[5] = 0.0f;
421 | }
422 |
423 | static void nsvg__xformMultiply(float* t, float* s)
424 | {
425 | float t0 = t[0] * s[0] + t[1] * s[2];
426 | float t2 = t[2] * s[0] + t[3] * s[2];
427 | float t4 = t[4] * s[0] + t[5] * s[2] + s[4];
428 | t[1] = t[0] * s[1] + t[1] * s[3];
429 | t[3] = t[2] * s[1] + t[3] * s[3];
430 | t[5] = t[4] * s[1] + t[5] * s[3] + s[5];
431 | t[0] = t0;
432 | t[2] = t2;
433 | t[4] = t4;
434 | }
435 |
436 | static void nsvg__xformInverse(float* inv, float* t)
437 | {
438 | double det = (double)t[0] * t[3] - (double)t[2] * t[1];
439 | double invdet = 1.0 / det;
440 | if (det > -1e-6 && det < -1e-6) {
441 | nsvg__xformIdentity(t);
442 | return;
443 | }
444 | inv[0] = (float)(t[3] * invdet);
445 | inv[2] = (float)(-t[2] * invdet);
446 | inv[4] = (float)(((double)t[2] * t[5] - (double)t[3] * t[4]) * invdet);
447 | inv[1] = (float)(-t[1] * invdet);
448 | inv[3] = (float)(t[0] * invdet);
449 | inv[5] = (float)(((double)t[1] * t[4] - (double)t[0] * t[5]) * invdet);
450 | }
451 |
452 | static void nsvg__xformPremultiply(float* t, float* s)
453 | {
454 | float s2[6];
455 | memcpy(s2, s, sizeof(float)*6);
456 | nsvg__xformMultiply(s2, t);
457 | memcpy(t, s2, sizeof(float)*6);
458 | }
459 |
460 | static void nsvg__xformPoint(float* dx, float* dy, float x, float y, float* t)
461 | {
462 | *dx = x*t[0] + y*t[2] + t[4];
463 | *dy = x*t[1] + y*t[3] + t[5];
464 | }
465 |
466 | static void nsvg__xformVec(float* dx, float* dy, float x, float y, float* t)
467 | {
468 | *dx = x*t[0] + y*t[2];
469 | *dy = x*t[1] + y*t[3];
470 | }
471 |
472 | #define NSVG_EPSILON (1e-12)
473 |
474 | static int nsvg__ptInBounds(float* pt, float* bounds)
475 | {
476 | return pt[0] >= bounds[0] && pt[0] <= bounds[2] && pt[1] >= bounds[1] && pt[1] <= bounds[3];
477 | }
478 |
479 |
480 | static double nsvg__evalBezier(double t, double p0, double p1, double p2, double p3)
481 | {
482 | double it = 1.0-t;
483 | return it*it*it*p0 + 3.0*it*it*t*p1 + 3.0*it*t*t*p2 + t*t*t*p3;
484 | }
485 |
486 | static void nsvg__curveBounds(float* bounds, float* curve)
487 | {
488 | int i, j, count;
489 | double roots[2], a, b, c, b2ac, t, v;
490 | float* v0 = &curve[0];
491 | float* v1 = &curve[2];
492 | float* v2 = &curve[4];
493 | float* v3 = &curve[6];
494 |
495 | // Start the bounding box by end points
496 | bounds[0] = nsvg__minf(v0[0], v3[0]);
497 | bounds[1] = nsvg__minf(v0[1], v3[1]);
498 | bounds[2] = nsvg__maxf(v0[0], v3[0]);
499 | bounds[3] = nsvg__maxf(v0[1], v3[1]);
500 |
501 | // Bezier curve fits inside the convex hull of it's control points.
502 | // If control points are inside the bounds, we're done.
503 | if (nsvg__ptInBounds(v1, bounds) && nsvg__ptInBounds(v2, bounds))
504 | return;
505 |
506 | // Add bezier curve inflection points in X and Y.
507 | for (i = 0; i < 2; i++) {
508 | a = -3.0 * v0[i] + 9.0 * v1[i] - 9.0 * v2[i] + 3.0 * v3[i];
509 | b = 6.0 * v0[i] - 12.0 * v1[i] + 6.0 * v2[i];
510 | c = 3.0 * v1[i] - 3.0 * v0[i];
511 | count = 0;
512 | if (fabs(a) < NSVG_EPSILON) {
513 | if (fabs(b) > NSVG_EPSILON) {
514 | t = -c / b;
515 | if (t > NSVG_EPSILON && t < 1.0-NSVG_EPSILON)
516 | roots[count++] = t;
517 | }
518 | } else {
519 | b2ac = b*b - 4.0*c*a;
520 | if (b2ac > NSVG_EPSILON) {
521 | t = (-b + sqrt(b2ac)) / (2.0 * a);
522 | if (t > NSVG_EPSILON && t < 1.0-NSVG_EPSILON)
523 | roots[count++] = t;
524 | t = (-b - sqrt(b2ac)) / (2.0 * a);
525 | if (t > NSVG_EPSILON && t < 1.0-NSVG_EPSILON)
526 | roots[count++] = t;
527 | }
528 | }
529 | for (j = 0; j < count; j++) {
530 | v = nsvg__evalBezier(roots[j], v0[i], v1[i], v2[i], v3[i]);
531 | bounds[0+i] = nsvg__minf(bounds[0+i], (float)v);
532 | bounds[2+i] = nsvg__maxf(bounds[2+i], (float)v);
533 | }
534 | }
535 | }
536 |
537 | static NSVGparser* nsvg__createParser()
538 | {
539 | NSVGparser* p;
540 | p = (NSVGparser*)malloc(sizeof(NSVGparser));
541 | if (p == NULL) goto error;
542 | memset(p, 0, sizeof(NSVGparser));
543 |
544 | p->image = (NSVGimage*)malloc(sizeof(NSVGimage));
545 | if (p->image == NULL) goto error;
546 | memset(p->image, 0, sizeof(NSVGimage));
547 |
548 | // Init style
549 | nsvg__xformIdentity(p->attr[0].xform);
550 | p->attr[0].fillColor = NSVG_RGB(0,0,0);
551 | p->attr[0].strokeColor = NSVG_RGB(0,0,0);
552 | p->attr[0].opacity = 1;
553 | p->attr[0].fillOpacity = 1;
554 | p->attr[0].strokeOpacity = 1;
555 | p->attr[0].stopOpacity = 1;
556 | p->attr[0].strokeWidth = 1;
557 | p->attr[0].hasFill = 1;
558 | p->attr[0].hasStroke = 0;
559 | p->attr[0].visible = 1;
560 |
561 | return p;
562 |
563 | error:
564 | if (p) {
565 | if (p->image) free(p->image);
566 | free(p);
567 | }
568 | return NULL;
569 | }
570 |
571 | static void nsvg__deletePaths(NSVGpath* path)
572 | {
573 | while (path) {
574 | NSVGpath *next = path->next;
575 | if (path->pts != NULL)
576 | free(path->pts);
577 | free(path);
578 | path = next;
579 | }
580 | }
581 |
582 | static void nsvg__deletePaint(NSVGpaint* paint)
583 | {
584 | if (paint->type == NSVG_PAINT_LINEAR_GRADIENT || paint->type == NSVG_PAINT_LINEAR_GRADIENT)
585 | free(paint->gradient);
586 | }
587 |
588 | static void nsvg__deleteGradientData(NSVGgradientData* grad)
589 | {
590 | NSVGgradientData* next;
591 | while (grad != NULL) {
592 | next = grad->next;
593 | free(grad->stops);
594 | free(grad);
595 | grad = next;
596 | }
597 | }
598 |
599 | static void nsvg__deleteParser(NSVGparser* p)
600 | {
601 | if (p != NULL) {
602 | nsvg__deletePaths(p->plist);
603 | nsvg__deleteGradientData(p->gradients);
604 | nsvgDelete(p->image);
605 | free(p->pts);
606 | free(p);
607 | }
608 | }
609 |
610 | static void nsvg__resetPath(NSVGparser* p)
611 | {
612 | p->npts = 0;
613 | }
614 |
615 | static void nsvg__addPoint(NSVGparser* p, float x, float y)
616 | {
617 | if (p->npts+1 > p->cpts) {
618 | p->cpts = p->cpts ? p->cpts*2 : 8;
619 | p->pts = (float*)realloc(p->pts, p->cpts*2*sizeof(float));
620 | if (!p->pts) return;
621 | }
622 | p->pts[p->npts*2+0] = x;
623 | p->pts[p->npts*2+1] = y;
624 | p->npts++;
625 | }
626 |
627 | static void nsvg__moveTo(NSVGparser* p, float x, float y)
628 | {
629 | if (p->npts > 0) {
630 | p->pts[(p->npts-1)*2+0] = x;
631 | p->pts[(p->npts-1)*2+1] = y;
632 | } else {
633 | nsvg__addPoint(p, x, y);
634 | }
635 | }
636 |
637 | static void nsvg__lineTo(NSVGparser* p, float x, float y)
638 | {
639 | float px,py, dx,dy;
640 | if (p->npts > 0) {
641 | px = p->pts[(p->npts-1)*2+0];
642 | py = p->pts[(p->npts-1)*2+1];
643 | dx = x - px;
644 | dy = y - py;
645 | nsvg__addPoint(p, px + dx/3.0f, py + dy/3.0f);
646 | nsvg__addPoint(p, x - dx/3.0f, y - dy/3.0f);
647 | nsvg__addPoint(p, x, y);
648 | }
649 | }
650 |
651 | static void nsvg__cubicBezTo(NSVGparser* p, float cpx1, float cpy1, float cpx2, float cpy2, float x, float y)
652 | {
653 | nsvg__addPoint(p, cpx1, cpy1);
654 | nsvg__addPoint(p, cpx2, cpy2);
655 | nsvg__addPoint(p, x, y);
656 | }
657 |
658 | static NSVGattrib* nsvg__getAttr(NSVGparser* p)
659 | {
660 | return &p->attr[p->attrHead];
661 | }
662 |
663 | static void nsvg__pushAttr(NSVGparser* p)
664 | {
665 | if (p->attrHead < NSVG_MAX_ATTR-1) {
666 | p->attrHead++;
667 | memcpy(&p->attr[p->attrHead], &p->attr[p->attrHead-1], sizeof(NSVGattrib));
668 | }
669 | }
670 |
671 | static void nsvg__popAttr(NSVGparser* p)
672 | {
673 | if (p->attrHead > 0)
674 | p->attrHead--;
675 | }
676 |
677 | static NSVGgradientData* nsvg__findGradientData(NSVGparser* p, const char* id)
678 | {
679 | NSVGgradientData* grad = p->gradients;
680 | while (grad) {
681 | if (strcmp(grad->id, id) == 0)
682 | return grad;
683 | grad = grad->next;
684 | }
685 | return NULL;
686 | }
687 |
688 | static NSVGgradient* nsvg__createGradient(NSVGparser* p, const char* id, const float* bounds, char* paintType)
689 | {
690 | NSVGattrib* attr = nsvg__getAttr(p);
691 | NSVGgradientData* data = NULL;
692 | NSVGgradientData* ref = NULL;
693 | NSVGgradientStop* stops = NULL;
694 | NSVGgradient* grad;
695 | float dx, dy, d;
696 | int nstops = 0;
697 | NSVG_NOTUSED(bounds);
698 |
699 | data = nsvg__findGradientData(p, id);
700 | if (data == NULL) return NULL;
701 |
702 | // TODO: use ref to fill in all unset values too.
703 | ref = data;
704 | while (ref != NULL) {
705 | if (ref->stops != NULL) {
706 | stops = ref->stops;
707 | nstops = ref->nstops;
708 | break;
709 | }
710 | ref = nsvg__findGradientData(p, ref->ref);
711 | }
712 | if (stops == NULL) return NULL;
713 |
714 | grad = (NSVGgradient*)malloc(sizeof(NSVGgradient) + sizeof(NSVGgradientStop)*(nstops-1));
715 | if (grad == NULL) return NULL;
716 |
717 | // TODO: handle data->units == NSVG_OBJECT_SPACE.
718 |
719 | if (data->type == NSVG_PAINT_LINEAR_GRADIENT) {
720 | // Calculate transform aligned to the line
721 | dx = data->linear.x2 - data->linear.x1;
722 | dy = data->linear.y2 - data->linear.y1;
723 | d = sqrtf(dx*dx + dy*dy);
724 | grad->xform[0] = dy; grad->xform[1] = -dx;
725 | grad->xform[2] = dx; grad->xform[3] = dy;
726 | grad->xform[4] = data->linear.x1; grad->xform[5] = data->linear.y1;
727 | } else {
728 | // Calculate transform aligned to the circle
729 | grad->xform[0] = data->radial.r; grad->xform[1] = 0;
730 | grad->xform[2] = 0; grad->xform[3] = data->radial.r;
731 | grad->xform[4] = data->radial.cx; grad->xform[5] = data->radial.cy;
732 | grad->fx = data->radial.fx / data->radial.r;
733 | grad->fy = data->radial.fy / data->radial.r;
734 | }
735 |
736 | nsvg__xformMultiply(grad->xform, attr->xform);
737 | nsvg__xformMultiply(grad->xform, data->xform);
738 |
739 | grad->spread = data->spread;
740 | memcpy(grad->stops, stops, nstops*sizeof(NSVGgradientStop));
741 | grad->nstops = nstops;
742 |
743 | *paintType = data->type;
744 |
745 | return grad;
746 | }
747 |
748 | static void nsvg__addShape(NSVGparser* p)
749 | {
750 | NSVGattrib* attr = nsvg__getAttr(p);
751 | float scale = 1.0f;
752 | NSVGshape *shape, *cur, *prev;
753 | NSVGpath* path;
754 |
755 | if (p->plist == NULL)
756 | return;
757 |
758 | shape = (NSVGshape*)malloc(sizeof(NSVGshape));
759 | if (shape == NULL) goto error;
760 | memset(shape, 0, sizeof(NSVGshape));
761 |
762 | scale = nsvg__maxf(fabsf(attr->xform[0]), fabsf(attr->xform[3]));
763 | shape->strokeWidth = attr->strokeWidth * scale;
764 | shape->opacity = attr->opacity;
765 |
766 | shape->paths = p->plist;
767 | p->plist = NULL;
768 |
769 | // Calculate shape bounds
770 | shape->bounds[0] = shape->paths->bounds[0];
771 | shape->bounds[1] = shape->paths->bounds[1];
772 | shape->bounds[2] = shape->paths->bounds[2];
773 | shape->bounds[3] = shape->paths->bounds[3];
774 | for (path = shape->paths->next; path != NULL; path = path->next) {
775 | shape->bounds[0] = nsvg__minf(shape->bounds[0], path->bounds[0]);
776 | shape->bounds[1] = nsvg__minf(shape->bounds[1], path->bounds[1]);
777 | shape->bounds[2] = nsvg__maxf(shape->bounds[2], path->bounds[2]);
778 | shape->bounds[3] = nsvg__maxf(shape->bounds[3], path->bounds[3]);
779 | }
780 |
781 | // Set fill
782 | if (attr->hasFill == 0) {
783 | shape->fill.type = NSVG_PAINT_NONE;
784 | } else if (attr->hasFill == 1) {
785 | shape->fill.type = NSVG_PAINT_COLOR;
786 | shape->fill.color = attr->fillColor;
787 | shape->fill.color |= (unsigned int)(attr->fillOpacity*255) << 24;
788 | } else if (attr->hasFill == 2) {
789 | shape->fill.gradient = nsvg__createGradient(p, attr->fillGradient, shape->bounds, &shape->fill.type);
790 | if (shape->fill.gradient == NULL) {
791 | shape->fill.type = NSVG_PAINT_NONE;
792 | }
793 | }
794 |
795 | // Set stroke
796 | if (attr->hasStroke == 0) {
797 | shape->stroke.type = NSVG_PAINT_NONE;
798 | } else if (attr->hasStroke == 1) {
799 | shape->stroke.type = NSVG_PAINT_COLOR;
800 | shape->stroke.color = attr->strokeColor;
801 | shape->stroke.color |= (unsigned int)(attr->strokeOpacity*255) << 24;
802 | } else if (attr->hasStroke == 2) {
803 | shape->stroke.gradient = nsvg__createGradient(p, attr->strokeGradient, shape->bounds, &shape->stroke.type);
804 | if (shape->stroke.gradient == NULL)
805 | shape->stroke.type = NSVG_PAINT_NONE;
806 | }
807 |
808 | // Add to tail
809 | prev = NULL;
810 | cur = p->image->shapes;
811 | while (cur != NULL) {
812 | prev = cur;
813 | cur = cur->next;
814 | }
815 | if (prev == NULL)
816 | p->image->shapes = shape;
817 | else
818 | prev->next = shape;
819 |
820 | return;
821 |
822 | error:
823 | if (shape) free(shape);
824 | }
825 |
826 | static void nsvg__addPath(NSVGparser* p, char closed)
827 | {
828 | NSVGattrib* attr = nsvg__getAttr(p);
829 | NSVGpath* path = NULL;
830 | float bounds[4];
831 | float* curve;
832 | int i;
833 |
834 | if (p->npts < 4)
835 | return;
836 |
837 | if (closed)
838 | nsvg__lineTo(p, p->pts[0], p->pts[1]);
839 |
840 | path = (NSVGpath*)malloc(sizeof(NSVGpath));
841 | if (path == NULL) goto error;
842 | memset(path, 0, sizeof(NSVGpath));
843 |
844 | path->pts = (float*)malloc(p->npts*2*sizeof(float));
845 | if (path->pts == NULL) goto error;
846 | path->closed = closed;
847 | path->npts = p->npts;
848 |
849 | // Transform path.
850 | for (i = 0; i < p->npts; ++i)
851 | nsvg__xformPoint(&path->pts[i*2], &path->pts[i*2+1], p->pts[i*2], p->pts[i*2+1], attr->xform);
852 |
853 | // Find bounds
854 | for (i = 0; i < path->npts-1; i += 3) {
855 | curve = &path->pts[i*2];
856 | nsvg__curveBounds(bounds, curve);
857 | if (i == 0) {
858 | path->bounds[0] = bounds[0];
859 | path->bounds[1] = bounds[1];
860 | path->bounds[2] = bounds[2];
861 | path->bounds[3] = bounds[3];
862 | } else {
863 | path->bounds[0] = nsvg__minf(path->bounds[0], bounds[0]);
864 | path->bounds[1] = nsvg__minf(path->bounds[1], bounds[1]);
865 | path->bounds[2] = nsvg__maxf(path->bounds[2], bounds[2]);
866 | path->bounds[3] = nsvg__maxf(path->bounds[3], bounds[3]);
867 | }
868 | }
869 |
870 | path->next = p->plist;
871 | p->plist = path;
872 |
873 | return;
874 |
875 | error:
876 | if (path != NULL) {
877 | if (path->pts != NULL) free(path->pts);
878 | free(path);
879 | }
880 | }
881 |
882 | static const char* nsvg__getNextPathItem(const char* s, char* it)
883 | {
884 | int i = 0;
885 | it[0] = '\0';
886 | // Skip white spaces and commas
887 | while (*s && (nsvg__isspace(*s) || *s == ',')) s++;
888 | if (!*s) return s;
889 | if (*s == '-' || *s == '+' || nsvg__isdigit(*s)) {
890 | // sign
891 | if (*s == '-' || *s == '+') {
892 | if (i < 63) it[i++] = *s;
893 | s++;
894 | }
895 | // integer part
896 | while (*s && nsvg__isdigit(*s)) {
897 | if (i < 63) it[i++] = *s;
898 | s++;
899 | }
900 | if (*s == '.') {
901 | // decimal point
902 | if (i < 63) it[i++] = *s;
903 | s++;
904 | // fraction part
905 | while (*s && nsvg__isdigit(*s)) {
906 | if (i < 63) it[i++] = *s;
907 | s++;
908 | }
909 | }
910 | // exponent
911 | if (*s == 'e' || *s == 'E') {
912 | if (i < 63) it[i++] = *s;
913 | s++;
914 | if (*s == '-' || *s == '+') {
915 | if (i < 63) it[i++] = *s;
916 | s++;
917 | }
918 | while (*s && nsvg__isdigit(*s)) {
919 | if (i < 63) it[i++] = *s;
920 | s++;
921 | }
922 | }
923 | it[i] = '\0';
924 | } else {
925 | // Parse command
926 | it[0] = *s++;
927 | it[1] = '\0';
928 | return s;
929 | }
930 |
931 | return s;
932 | }
933 |
934 | static float nsvg__actualWidth(NSVGparser* p)
935 | {
936 | return p->viewWidth;
937 | }
938 |
939 | static float nsvg__actualHeight(NSVGparser* p)
940 | {
941 | return p->viewHeight;
942 | }
943 |
944 | static float nsvg__actualLength(NSVGparser* p)
945 | {
946 | float w = nsvg__actualWidth(p), h = nsvg__actualHeight(p);
947 | return sqrtf(w*w + h*h) / sqrtf(2.0f);
948 | }
949 |
950 |
951 | static unsigned int nsvg__parseColorHex(const char* str)
952 | {
953 | unsigned int c = 0, r = 0, g = 0, b = 0;
954 | int n = 0;
955 | str++; // skip #
956 | // Calculate number of characters.
957 | while(str[n] && !nsvg__isspace(str[n]))
958 | n++;
959 | if (n == 6) {
960 | sscanf(str, "%x", &c);
961 | } else if (n == 3) {
962 | sscanf(str, "%x", &c);
963 | c = (c&0xf) | ((c&0xf0) << 4) | ((c&0xf00) << 8);
964 | c |= c<<4;
965 | }
966 | r = (c >> 16) & 0xff;
967 | g = (c >> 8) & 0xff;
968 | b = c & 0xff;
969 | return NSVG_RGB(r,g,b);
970 | }
971 |
972 | static unsigned int nsvg__parseColorRGB(const char* str)
973 | {
974 | int r = -1, g = -1, b = -1;
975 | char s1[32]="", s2[32]="";
976 | sscanf(str + 4, "%d%[%%, \t]%d%[%%, \t]%d", &r, s1, &g, s2, &b);
977 | if (strchr(s1, '%')) {
978 | return NSVG_RGB((r*255)/100,(g*255)/100,(b*255)/100);
979 | } else {
980 | return NSVG_RGB(r,g,b);
981 | }
982 | }
983 |
984 | typedef struct NSVGNamedColor {
985 | const char* name;
986 | unsigned int color;
987 | } NSVGNamedColor;
988 |
989 | NSVGNamedColor nsvg__colors[] = {
990 |
991 | { "red", NSVG_RGB(255, 0, 0) },
992 | { "green", NSVG_RGB( 0, 128, 0) },
993 | { "blue", NSVG_RGB( 0, 0, 255) },
994 | { "yellow", NSVG_RGB(255, 255, 0) },
995 | { "cyan", NSVG_RGB( 0, 255, 255) },
996 | { "magenta", NSVG_RGB(255, 0, 255) },
997 | { "black", NSVG_RGB( 0, 0, 0) },
998 | { "grey", NSVG_RGB(128, 128, 128) },
999 | { "gray", NSVG_RGB(128, 128, 128) },
1000 | { "white", NSVG_RGB(255, 255, 255) },
1001 |
1002 | #ifdef NANOSVG_ALL_COLOR_KEYWORDS
1003 | { "aliceblue", NSVG_RGB(240, 248, 255) },
1004 | { "antiquewhite", NSVG_RGB(250, 235, 215) },
1005 | { "aqua", NSVG_RGB( 0, 255, 255) },
1006 | { "aquamarine", NSVG_RGB(127, 255, 212) },
1007 | { "azure", NSVG_RGB(240, 255, 255) },
1008 | { "beige", NSVG_RGB(245, 245, 220) },
1009 | { "bisque", NSVG_RGB(255, 228, 196) },
1010 | { "blanchedalmond", NSVG_RGB(255, 235, 205) },
1011 | { "blueviolet", NSVG_RGB(138, 43, 226) },
1012 | { "brown", NSVG_RGB(165, 42, 42) },
1013 | { "burlywood", NSVG_RGB(222, 184, 135) },
1014 | { "cadetblue", NSVG_RGB( 95, 158, 160) },
1015 | { "chartreuse", NSVG_RGB(127, 255, 0) },
1016 | { "chocolate", NSVG_RGB(210, 105, 30) },
1017 | { "coral", NSVG_RGB(255, 127, 80) },
1018 | { "cornflowerblue", NSVG_RGB(100, 149, 237) },
1019 | { "cornsilk", NSVG_RGB(255, 248, 220) },
1020 | { "crimson", NSVG_RGB(220, 20, 60) },
1021 | { "darkblue", NSVG_RGB( 0, 0, 139) },
1022 | { "darkcyan", NSVG_RGB( 0, 139, 139) },
1023 | { "darkgoldenrod", NSVG_RGB(184, 134, 11) },
1024 | { "darkgray", NSVG_RGB(169, 169, 169) },
1025 | { "darkgreen", NSVG_RGB( 0, 100, 0) },
1026 | { "darkgrey", NSVG_RGB(169, 169, 169) },
1027 | { "darkkhaki", NSVG_RGB(189, 183, 107) },
1028 | { "darkmagenta", NSVG_RGB(139, 0, 139) },
1029 | { "darkolivegreen", NSVG_RGB( 85, 107, 47) },
1030 | { "darkorange", NSVG_RGB(255, 140, 0) },
1031 | { "darkorchid", NSVG_RGB(153, 50, 204) },
1032 | { "darkred", NSVG_RGB(139, 0, 0) },
1033 | { "darksalmon", NSVG_RGB(233, 150, 122) },
1034 | { "darkseagreen", NSVG_RGB(143, 188, 143) },
1035 | { "darkslateblue", NSVG_RGB( 72, 61, 139) },
1036 | { "darkslategray", NSVG_RGB( 47, 79, 79) },
1037 | { "darkslategrey", NSVG_RGB( 47, 79, 79) },
1038 | { "darkturquoise", NSVG_RGB( 0, 206, 209) },
1039 | { "darkviolet", NSVG_RGB(148, 0, 211) },
1040 | { "deeppink", NSVG_RGB(255, 20, 147) },
1041 | { "deepskyblue", NSVG_RGB( 0, 191, 255) },
1042 | { "dimgray", NSVG_RGB(105, 105, 105) },
1043 | { "dimgrey", NSVG_RGB(105, 105, 105) },
1044 | { "dodgerblue", NSVG_RGB( 30, 144, 255) },
1045 | { "firebrick", NSVG_RGB(178, 34, 34) },
1046 | { "floralwhite", NSVG_RGB(255, 250, 240) },
1047 | { "forestgreen", NSVG_RGB( 34, 139, 34) },
1048 | { "fuchsia", NSVG_RGB(255, 0, 255) },
1049 | { "gainsboro", NSVG_RGB(220, 220, 220) },
1050 | { "ghostwhite", NSVG_RGB(248, 248, 255) },
1051 | { "gold", NSVG_RGB(255, 215, 0) },
1052 | { "goldenrod", NSVG_RGB(218, 165, 32) },
1053 | { "greenyellow", NSVG_RGB(173, 255, 47) },
1054 | { "honeydew", NSVG_RGB(240, 255, 240) },
1055 | { "hotpink", NSVG_RGB(255, 105, 180) },
1056 | { "indianred", NSVG_RGB(205, 92, 92) },
1057 | { "indigo", NSVG_RGB( 75, 0, 130) },
1058 | { "ivory", NSVG_RGB(255, 255, 240) },
1059 | { "khaki", NSVG_RGB(240, 230, 140) },
1060 | { "lavender", NSVG_RGB(230, 230, 250) },
1061 | { "lavenderblush", NSVG_RGB(255, 240, 245) },
1062 | { "lawngreen", NSVG_RGB(124, 252, 0) },
1063 | { "lemonchiffon", NSVG_RGB(255, 250, 205) },
1064 | { "lightblue", NSVG_RGB(173, 216, 230) },
1065 | { "lightcoral", NSVG_RGB(240, 128, 128) },
1066 | { "lightcyan", NSVG_RGB(224, 255, 255) },
1067 | { "lightgoldenrodyellow", NSVG_RGB(250, 250, 210) },
1068 | { "lightgray", NSVG_RGB(211, 211, 211) },
1069 | { "lightgreen", NSVG_RGB(144, 238, 144) },
1070 | { "lightgrey", NSVG_RGB(211, 211, 211) },
1071 | { "lightpink", NSVG_RGB(255, 182, 193) },
1072 | { "lightsalmon", NSVG_RGB(255, 160, 122) },
1073 | { "lightseagreen", NSVG_RGB( 32, 178, 170) },
1074 | { "lightskyblue", NSVG_RGB(135, 206, 250) },
1075 | { "lightslategray", NSVG_RGB(119, 136, 153) },
1076 | { "lightslategrey", NSVG_RGB(119, 136, 153) },
1077 | { "lightsteelblue", NSVG_RGB(176, 196, 222) },
1078 | { "lightyellow", NSVG_RGB(255, 255, 224) },
1079 | { "lime", NSVG_RGB( 0, 255, 0) },
1080 | { "limegreen", NSVG_RGB( 50, 205, 50) },
1081 | { "linen", NSVG_RGB(250, 240, 230) },
1082 | { "maroon", NSVG_RGB(128, 0, 0) },
1083 | { "mediumaquamarine", NSVG_RGB(102, 205, 170) },
1084 | { "mediumblue", NSVG_RGB( 0, 0, 205) },
1085 | { "mediumorchid", NSVG_RGB(186, 85, 211) },
1086 | { "mediumpurple", NSVG_RGB(147, 112, 219) },
1087 | { "mediumseagreen", NSVG_RGB( 60, 179, 113) },
1088 | { "mediumslateblue", NSVG_RGB(123, 104, 238) },
1089 | { "mediumspringgreen", NSVG_RGB( 0, 250, 154) },
1090 | { "mediumturquoise", NSVG_RGB( 72, 209, 204) },
1091 | { "mediumvioletred", NSVG_RGB(199, 21, 133) },
1092 | { "midnightblue", NSVG_RGB( 25, 25, 112) },
1093 | { "mintcream", NSVG_RGB(245, 255, 250) },
1094 | { "mistyrose", NSVG_RGB(255, 228, 225) },
1095 | { "moccasin", NSVG_RGB(255, 228, 181) },
1096 | { "navajowhite", NSVG_RGB(255, 222, 173) },
1097 | { "navy", NSVG_RGB( 0, 0, 128) },
1098 | { "oldlace", NSVG_RGB(253, 245, 230) },
1099 | { "olive", NSVG_RGB(128, 128, 0) },
1100 | { "olivedrab", NSVG_RGB(107, 142, 35) },
1101 | { "orange", NSVG_RGB(255, 165, 0) },
1102 | { "orangered", NSVG_RGB(255, 69, 0) },
1103 | { "orchid", NSVG_RGB(218, 112, 214) },
1104 | { "palegoldenrod", NSVG_RGB(238, 232, 170) },
1105 | { "palegreen", NSVG_RGB(152, 251, 152) },
1106 | { "paleturquoise", NSVG_RGB(175, 238, 238) },
1107 | { "palevioletred", NSVG_RGB(219, 112, 147) },
1108 | { "papayawhip", NSVG_RGB(255, 239, 213) },
1109 | { "peachpuff", NSVG_RGB(255, 218, 185) },
1110 | { "peru", NSVG_RGB(205, 133, 63) },
1111 | { "pink", NSVG_RGB(255, 192, 203) },
1112 | { "plum", NSVG_RGB(221, 160, 221) },
1113 | { "powderblue", NSVG_RGB(176, 224, 230) },
1114 | { "purple", NSVG_RGB(128, 0, 128) },
1115 | { "rosybrown", NSVG_RGB(188, 143, 143) },
1116 | { "royalblue", NSVG_RGB( 65, 105, 225) },
1117 | { "saddlebrown", NSVG_RGB(139, 69, 19) },
1118 | { "salmon", NSVG_RGB(250, 128, 114) },
1119 | { "sandybrown", NSVG_RGB(244, 164, 96) },
1120 | { "seagreen", NSVG_RGB( 46, 139, 87) },
1121 | { "seashell", NSVG_RGB(255, 245, 238) },
1122 | { "sienna", NSVG_RGB(160, 82, 45) },
1123 | { "silver", NSVG_RGB(192, 192, 192) },
1124 | { "skyblue", NSVG_RGB(135, 206, 235) },
1125 | { "slateblue", NSVG_RGB(106, 90, 205) },
1126 | { "slategray", NSVG_RGB(112, 128, 144) },
1127 | { "slategrey", NSVG_RGB(112, 128, 144) },
1128 | { "snow", NSVG_RGB(255, 250, 250) },
1129 | { "springgreen", NSVG_RGB( 0, 255, 127) },
1130 | { "steelblue", NSVG_RGB( 70, 130, 180) },
1131 | { "tan", NSVG_RGB(210, 180, 140) },
1132 | { "teal", NSVG_RGB( 0, 128, 128) },
1133 | { "thistle", NSVG_RGB(216, 191, 216) },
1134 | { "tomato", NSVG_RGB(255, 99, 71) },
1135 | { "turquoise", NSVG_RGB( 64, 224, 208) },
1136 | { "violet", NSVG_RGB(238, 130, 238) },
1137 | { "wheat", NSVG_RGB(245, 222, 179) },
1138 | { "whitesmoke", NSVG_RGB(245, 245, 245) },
1139 | { "yellowgreen", NSVG_RGB(154, 205, 50) },
1140 | #endif
1141 | };
1142 |
1143 | static unsigned int nsvg__parseColorName(const char* str)
1144 | {
1145 | int i, ncolors = sizeof(nsvg__colors) / sizeof(NSVGNamedColor);
1146 |
1147 | for (i = 0; i < ncolors; i++) {
1148 | if (strcmp(nsvg__colors[i].name, str) == 0) {
1149 | return nsvg__colors[i].color;
1150 | }
1151 | }
1152 |
1153 | return NSVG_RGB(128, 128, 128);
1154 | }
1155 |
1156 | static unsigned int nsvg__parseColor(const char* str)
1157 | {
1158 | int len = 0;
1159 | while(*str == ' ') ++str;
1160 | len = strlen(str);
1161 | if (len >= 1 && *str == '#')
1162 | return nsvg__parseColorHex(str);
1163 | else if (len >= 4 && str[0] == 'r' && str[1] == 'g' && str[2] == 'b' && str[3] == '(')
1164 | return nsvg__parseColorRGB(str);
1165 | return nsvg__parseColorName(str);
1166 | }
1167 |
1168 | static float nsvg__convertToPixels(NSVGparser* p, float val, const char* units, int dir)
1169 | {
1170 | NSVGattrib* attr;
1171 |
1172 | if (p != NULL) {
1173 | // Convert units to pixels.
1174 | if (units[0] == '\0') {
1175 | return val;
1176 | } else if (units[0] == 'p' && units[1] == 'x') {
1177 | return val;
1178 | } else if (units[0] == 'p' && units[1] == 't') {
1179 | return val / 72.0f * p->dpi;
1180 | } else if (units[0] == 'p' && units[1] == 'c') {
1181 | return val / 6.0f * p->dpi;
1182 | } else if (units[0] == 'm' && units[1] == 'm') {
1183 | return val / 25.4f * p->dpi;
1184 | } else if (units[0] == 'c' && units[1] == 'm') {
1185 | return val / 2.54f * p->dpi;
1186 | } else if (units[0] == 'i' && units[1] == 'n') {
1187 | return val * p->dpi;
1188 | } else if (units[0] == '%') {
1189 | if (p != NULL) {
1190 | attr = nsvg__getAttr(p);
1191 | if (dir == 0)
1192 | return (val/100.0f) * nsvg__actualWidth(p);
1193 | else if (dir == 1)
1194 | return (val/100.0f) * nsvg__actualHeight(p);
1195 | else if (dir == 2)
1196 | return (val/100.0f) * nsvg__actualLength(p);
1197 | } else {
1198 | return (val/100.0f);
1199 | }
1200 | } else if (units[0] == 'e' && units[1] == 'm') {
1201 | if (p != NULL) {
1202 | attr = nsvg__getAttr(p);
1203 | return val * attr->fontSize;
1204 | }
1205 | } else if (units[0] == 'e' && units[1] == 'x') {
1206 | if (p != NULL) {
1207 | attr = nsvg__getAttr(p);
1208 | return val * attr->fontSize * 0.52f; // x-height of Helvetica.
1209 | }
1210 | }
1211 | } else {
1212 | // Convert units to pixels.
1213 | if (units[0] == '\0') {
1214 | return val;
1215 | } else if (units[0] == 'p' && units[1] == 'x') {
1216 | return val;
1217 | } else if (units[0] == '%') {
1218 | return (val/100.0f);
1219 | }
1220 | }
1221 | return val;
1222 | }
1223 |
1224 | static float nsvg__parseFloat(NSVGparser* p, const char* str, int dir)
1225 | {
1226 | float val = 0;
1227 | char units[32]="";
1228 | sscanf(str, "%f%s", &val, units);
1229 | return nsvg__convertToPixels(p, val, units, dir);
1230 | }
1231 |
1232 | static int nsvg__parseTransformArgs(const char* str, float* args, int maxNa, int* na)
1233 | {
1234 | const char* end;
1235 | const char* ptr;
1236 |
1237 | *na = 0;
1238 | ptr = str;
1239 | while (*ptr && *ptr != '(') ++ptr;
1240 | if (*ptr == 0)
1241 | return 1;
1242 | end = ptr;
1243 | while (*end && *end != ')') ++end;
1244 | if (*end == 0)
1245 | return 1;
1246 |
1247 | while (ptr < end) {
1248 | if (nsvg__isnum(*ptr)) {
1249 | if (*na >= maxNa) return 0;
1250 | args[(*na)++] = (float)atof(ptr);
1251 | while (ptr < end && nsvg__isnum(*ptr)) ++ptr;
1252 | } else {
1253 | ++ptr;
1254 | }
1255 | }
1256 | return (int)(end - str);
1257 | }
1258 |
1259 | static int nsvg__parseMatrix(float* xform, const char* str)
1260 | {
1261 | float t[6];
1262 | int na = 0;
1263 | int len = nsvg__parseTransformArgs(str, t, 6, &na);
1264 | if (na != 6) return len;
1265 | memcpy(xform, t, sizeof(float)*6);
1266 | return len;
1267 | }
1268 |
1269 | static int nsvg__parseTranslate(float* xform, const char* str)
1270 | {
1271 | float args[2];
1272 | float t[6];
1273 | int na = 0;
1274 | int len = nsvg__parseTransformArgs(str, args, 2, &na);
1275 | if (na == 1) args[1] = 0.0;
1276 |
1277 | nsvg__xformSetTranslation(t, args[0], args[1]);
1278 | memcpy(xform, t, sizeof(float)*6);
1279 | return len;
1280 | }
1281 |
1282 | static int nsvg__parseScale(float* xform, const char* str)
1283 | {
1284 | float args[2];
1285 | int na = 0;
1286 | float t[6];
1287 | int len = nsvg__parseTransformArgs(str, args, 2, &na);
1288 | if (na == 1) args[1] = args[0];
1289 | nsvg__xformSetScale(t, args[0], args[1]);
1290 | memcpy(xform, t, sizeof(float)*6);
1291 | return len;
1292 | }
1293 |
1294 | static int nsvg__parseSkewX(float* xform, const char* str)
1295 | {
1296 | float args[1];
1297 | int na = 0;
1298 | float t[6];
1299 | int len = nsvg__parseTransformArgs(str, args, 1, &na);
1300 | nsvg__xformSetSkewX(t, args[0]/180.0f*NSVG_PI);
1301 | memcpy(xform, t, sizeof(float)*6);
1302 | return len;
1303 | }
1304 |
1305 | static int nsvg__parseSkewY(float* xform, const char* str)
1306 | {
1307 | float args[1];
1308 | int na = 0;
1309 | float t[6];
1310 | int len = nsvg__parseTransformArgs(str, args, 1, &na);
1311 | nsvg__xformSetSkewY(t, args[0]/180.0f*NSVG_PI);
1312 | memcpy(xform, t, sizeof(float)*6);
1313 | return len;
1314 | }
1315 |
1316 | static int nsvg__parseRotate(float* xform, const char* str)
1317 | {
1318 | float args[3];
1319 | int na = 0;
1320 | float m[6];
1321 | float t[6];
1322 | int len = nsvg__parseTransformArgs(str, args, 3, &na);
1323 | if (na == 1)
1324 | args[1] = args[2] = 0.0f;
1325 | nsvg__xformIdentity(m);
1326 |
1327 | if (na > 1) {
1328 | nsvg__xformSetTranslation(t, -args[1], -args[2]);
1329 | nsvg__xformMultiply(m, t);
1330 | }
1331 |
1332 | nsvg__xformSetRotation(t, args[0]/180.0f*NSVG_PI);
1333 | nsvg__xformMultiply(m, t);
1334 |
1335 | if (na > 1) {
1336 | nsvg__xformSetTranslation(t, args[1], args[2]);
1337 | nsvg__xformMultiply(m, t);
1338 | }
1339 |
1340 | memcpy(xform, m, sizeof(float)*6);
1341 |
1342 | return len;
1343 | }
1344 |
1345 | static void nsvg__parseTransform(float* xform, const char* str)
1346 | {
1347 | float t[6];
1348 | nsvg__xformIdentity(xform);
1349 | while (*str)
1350 | {
1351 | if (strncmp(str, "matrix", 6) == 0)
1352 | str += nsvg__parseMatrix(t, str);
1353 | else if (strncmp(str, "translate", 9) == 0)
1354 | str += nsvg__parseTranslate(t, str);
1355 | else if (strncmp(str, "scale", 5) == 0)
1356 | str += nsvg__parseScale(t, str);
1357 | else if (strncmp(str, "rotate", 6) == 0)
1358 | str += nsvg__parseRotate(t, str);
1359 | else if (strncmp(str, "skewX", 5) == 0)
1360 | str += nsvg__parseSkewX(t, str);
1361 | else if (strncmp(str, "skewY", 5) == 0)
1362 | str += nsvg__parseSkewY(t, str);
1363 | else{
1364 | ++str;
1365 | continue;
1366 | }
1367 |
1368 | nsvg__xformPremultiply(xform, t);
1369 | }
1370 | }
1371 |
1372 | static void nsvg__parseUrl(char* id, const char* str)
1373 | {
1374 | int i = 0;
1375 | str += 4; // "url(";
1376 | if (*str == '#')
1377 | str++;
1378 | while (i < 63 && *str != ')') {
1379 | id[i] = *str++;
1380 | i++;
1381 | }
1382 | id[i] = '\0';
1383 | }
1384 |
1385 | static void nsvg__parseStyle(NSVGparser* p, const char* str);
1386 |
1387 | static int nsvg__parseAttr(NSVGparser* p, const char* name, const char* value)
1388 | {
1389 | float xform[6];
1390 | NSVGattrib* attr = nsvg__getAttr(p);
1391 | if (!attr) return 0;
1392 |
1393 | if (strcmp(name, "style") == 0) {
1394 | nsvg__parseStyle(p, value);
1395 | } else if (strcmp(name, "display") == 0) {
1396 | if (strcmp(value, "none") == 0)
1397 | attr->visible = 0;
1398 | else
1399 | attr->visible = 1;
1400 | } else if (strcmp(name, "fill") == 0) {
1401 | if (strcmp(value, "none") == 0) {
1402 | attr->hasFill = 0;
1403 | } else if (strncmp(value, "url(", 4) == 0) {
1404 | attr->hasFill = 2;
1405 | nsvg__parseUrl(attr->fillGradient, value);
1406 | } else {
1407 | attr->hasFill = 1;
1408 | attr->fillColor = nsvg__parseColor(value);
1409 | }
1410 | } else if (strcmp(name, "opacity") == 0) {
1411 | attr->opacity = nsvg__parseFloat(p, value, 2);
1412 | } else if (strcmp(name, "fill-opacity") == 0) {
1413 | attr->fillOpacity = nsvg__parseFloat(p, value, 2);
1414 | } else if (strcmp(name, "stroke") == 0) {
1415 | if (strcmp(value, "none") == 0) {
1416 | attr->hasStroke = 0;
1417 | } else if (strncmp(value, "url(", 4) == 0) {
1418 | attr->hasStroke = 2;
1419 | nsvg__parseUrl(attr->strokeGradient, value);
1420 | } else {
1421 | attr->hasStroke = 1;
1422 | attr->strokeColor = nsvg__parseColor(value);
1423 | }
1424 | } else if (strcmp(name, "stroke-width") == 0) {
1425 | attr->strokeWidth = nsvg__parseFloat(p, value, 2);
1426 | } else if (strcmp(name, "stroke-opacity") == 0) {
1427 | attr->strokeOpacity = nsvg__parseFloat(NULL, value, 2);
1428 | } else if (strcmp(name, "font-size") == 0) {
1429 | attr->fontSize = nsvg__parseFloat(p, value, 2);
1430 | } else if (strcmp(name, "transform") == 0) {
1431 | nsvg__parseTransform(xform, value);
1432 | nsvg__xformPremultiply(attr->xform, xform);
1433 | } else if (strcmp(name, "stop-color") == 0) {
1434 | attr->stopColor = nsvg__parseColor(value);
1435 | } else if (strcmp(name, "stop-opacity") == 0) {
1436 | attr->stopOpacity = nsvg__parseFloat(NULL, value, 2);
1437 | } else if (strcmp(name, "offset") == 0) {
1438 | attr->stopOffset = nsvg__parseFloat(NULL, value, 2);
1439 | } else {
1440 | return 0;
1441 | }
1442 | return 1;
1443 | }
1444 |
1445 | static int nsvg__parseNameValue(NSVGparser* p, const char* start, const char* end)
1446 | {
1447 | const char* str;
1448 | const char* val;
1449 | char name[512];
1450 | char value[512];
1451 | int n;
1452 |
1453 | str = start;
1454 | while (str < end && *str != ':') ++str;
1455 |
1456 | val = str;
1457 |
1458 | // Right Trim
1459 | while (str > start && (*str == ':' || nsvg__isspace(*str))) --str;
1460 | ++str;
1461 |
1462 | n = (int)(str - start);
1463 | if (n > 511) n = 511;
1464 | if (n) memcpy(name, start, n);
1465 | name[n] = 0;
1466 |
1467 | while (val < end && (*val == ':' || nsvg__isspace(*val))) ++val;
1468 |
1469 | n = (int)(end - val);
1470 | if (n > 511) n = 511;
1471 | if (n) memcpy(value, val, n);
1472 | value[n] = 0;
1473 |
1474 | return nsvg__parseAttr(p, name, value);
1475 | }
1476 |
1477 | static void nsvg__parseStyle(NSVGparser* p, const char* str)
1478 | {
1479 | const char* start;
1480 | const char* end;
1481 |
1482 | while (*str) {
1483 | // Left Trim
1484 | while(*str && nsvg__isspace(*str)) ++str;
1485 | start = str;
1486 | while(*str && *str != ';') ++str;
1487 | end = str;
1488 |
1489 | // Right Trim
1490 | while (end > start && (*end == ';' || nsvg__isspace(*end))) --end;
1491 | ++end;
1492 |
1493 | nsvg__parseNameValue(p, start, end);
1494 | if (*str) ++str;
1495 | }
1496 | }
1497 |
1498 | static void nsvg__parseAttribs(NSVGparser* p, const char** attr)
1499 | {
1500 | int i;
1501 | for (i = 0; attr[i]; i += 2)
1502 | {
1503 | if (strcmp(attr[i], "style") == 0)
1504 | nsvg__parseStyle(p, attr[i + 1]);
1505 | else
1506 | nsvg__parseAttr(p, attr[i], attr[i + 1]);
1507 | }
1508 | }
1509 |
1510 | static int nsvg__getArgsPerElement(char cmd)
1511 | {
1512 | switch (cmd) {
1513 | case 'v':
1514 | case 'V':
1515 | case 'h':
1516 | case 'H':
1517 | return 1;
1518 | case 'm':
1519 | case 'M':
1520 | case 'l':
1521 | case 'L':
1522 | case 't':
1523 | case 'T':
1524 | return 2;
1525 | case 'q':
1526 | case 'Q':
1527 | case 's':
1528 | case 'S':
1529 | return 4;
1530 | case 'c':
1531 | case 'C':
1532 | return 6;
1533 | case 'a':
1534 | case 'A':
1535 | return 7;
1536 | }
1537 | return 0;
1538 | }
1539 |
1540 | static void nsvg__pathMoveTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel)
1541 | {
1542 | if (rel) {
1543 | *cpx += args[0];
1544 | *cpy += args[1];
1545 | } else {
1546 | *cpx = args[0];
1547 | *cpy = args[1];
1548 | }
1549 | nsvg__moveTo(p, *cpx, *cpy);
1550 | }
1551 |
1552 | static void nsvg__pathLineTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel)
1553 | {
1554 | if (rel) {
1555 | *cpx += args[0];
1556 | *cpy += args[1];
1557 | } else {
1558 | *cpx = args[0];
1559 | *cpy = args[1];
1560 | }
1561 | nsvg__lineTo(p, *cpx, *cpy);
1562 | }
1563 |
1564 | static void nsvg__pathHLineTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel)
1565 | {
1566 | if (rel)
1567 | *cpx += args[0];
1568 | else
1569 | *cpx = args[0];
1570 | nsvg__lineTo(p, *cpx, *cpy);
1571 | }
1572 |
1573 | static void nsvg__pathVLineTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel)
1574 | {
1575 | if (rel)
1576 | *cpy += args[0];
1577 | else
1578 | *cpy = args[0];
1579 | nsvg__lineTo(p, *cpx, *cpy);
1580 | }
1581 |
1582 | static void nsvg__pathCubicBezTo(NSVGparser* p, float* cpx, float* cpy,
1583 | float* cpx2, float* cpy2, float* args, int rel)
1584 | {
1585 | float x1, y1, x2, y2, cx1, cy1, cx2, cy2;
1586 |
1587 | x1 = *cpx;
1588 | y1 = *cpy;
1589 | if (rel) {
1590 | cx1 = *cpx + args[0];
1591 | cy1 = *cpy + args[1];
1592 | cx2 = *cpx + args[2];
1593 | cy2 = *cpy + args[3];
1594 | x2 = *cpx + args[4];
1595 | y2 = *cpy + args[5];
1596 | } else {
1597 | cx1 = args[0];
1598 | cy1 = args[1];
1599 | cx2 = args[2];
1600 | cy2 = args[3];
1601 | x2 = args[4];
1602 | y2 = args[5];
1603 | }
1604 |
1605 | nsvg__cubicBezTo(p, cx1,cy1, cx2,cy2, x2,y2);
1606 |
1607 | *cpx2 = cx2;
1608 | *cpy2 = cy2;
1609 | *cpx = x2;
1610 | *cpy = y2;
1611 | }
1612 |
1613 | static void nsvg__pathCubicBezShortTo(NSVGparser* p, float* cpx, float* cpy,
1614 | float* cpx2, float* cpy2, float* args, int rel)
1615 | {
1616 | float x1, y1, x2, y2, cx1, cy1, cx2, cy2;
1617 |
1618 | x1 = *cpx;
1619 | y1 = *cpy;
1620 | if (rel) {
1621 | cx2 = *cpx + args[0];
1622 | cy2 = *cpy + args[1];
1623 | x2 = *cpx + args[2];
1624 | y2 = *cpy + args[3];
1625 | } else {
1626 | cx2 = args[0];
1627 | cy2 = args[1];
1628 | x2 = args[2];
1629 | y2 = args[3];
1630 | }
1631 |
1632 | cx1 = 2*x1 - *cpx2;
1633 | cy1 = 2*y1 - *cpy2;
1634 |
1635 | nsvg__cubicBezTo(p, cx1,cy1, cx2,cy2, x2,y2);
1636 |
1637 | *cpx2 = cx2;
1638 | *cpy2 = cy2;
1639 | *cpx = x2;
1640 | *cpy = y2;
1641 | }
1642 |
1643 | static void nsvg__pathQuadBezTo(NSVGparser* p, float* cpx, float* cpy,
1644 | float* cpx2, float* cpy2, float* args, int rel)
1645 | {
1646 | float x1, y1, x2, y2, cx, cy;
1647 | float cx1, cy1, cx2, cy2;
1648 |
1649 | x1 = *cpx;
1650 | y1 = *cpy;
1651 | if (rel) {
1652 | cx = *cpx + args[0];
1653 | cy = *cpy + args[1];
1654 | x2 = *cpx + args[2];
1655 | y2 = *cpy + args[3];
1656 | } else {
1657 | cx = args[0];
1658 | cy = args[1];
1659 | x2 = args[2];
1660 | y2 = args[3];
1661 | }
1662 |
1663 | // Convert to cubic bezier
1664 | cx1 = x1 + 2.0f/3.0f*(cx - x1);
1665 | cy1 = y1 + 2.0f/3.0f*(cy - y1);
1666 | cx2 = x2 + 2.0f/3.0f*(cx - x2);
1667 | cy2 = y2 + 2.0f/3.0f*(cy - y2);
1668 |
1669 | nsvg__cubicBezTo(p, cx1,cy1, cx2,cy2, x2,y2);
1670 |
1671 | *cpx2 = cx;
1672 | *cpy2 = cy;
1673 | *cpx = x2;
1674 | *cpy = y2;
1675 | }
1676 |
1677 | static void nsvg__pathQuadBezShortTo(NSVGparser* p, float* cpx, float* cpy,
1678 | float* cpx2, float* cpy2, float* args, int rel)
1679 | {
1680 | float x1, y1, x2, y2, cx, cy;
1681 | float cx1, cy1, cx2, cy2;
1682 |
1683 | x1 = *cpx;
1684 | y1 = *cpy;
1685 | if (rel) {
1686 | x2 = *cpx + args[0];
1687 | y2 = *cpy + args[1];
1688 | } else {
1689 | x2 = args[0];
1690 | y2 = args[1];
1691 | }
1692 |
1693 | cx = 2*x1 - *cpx2;
1694 | cy = 2*y1 - *cpy2;
1695 |
1696 | // Convert to cubix bezier
1697 | cx1 = x1 + 2.0f/3.0f*(cx - x1);
1698 | cy1 = y1 + 2.0f/3.0f*(cy - y1);
1699 | cx2 = x2 + 2.0f/3.0f*(cx - x2);
1700 | cy2 = y2 + 2.0f/3.0f*(cy - y2);
1701 |
1702 | nsvg__cubicBezTo(p, cx1,cy1, cx2,cy2, x2,y2);
1703 |
1704 | *cpx2 = cx;
1705 | *cpy2 = cy;
1706 | *cpx = x2;
1707 | *cpy = y2;
1708 | }
1709 |
1710 | static float nsvg__sqr(float x) { return x*x; }
1711 | static float nsvg__vmag(float x, float y) { return sqrtf(x*x + y*y); }
1712 |
1713 | static float nsvg__vecrat(float ux, float uy, float vx, float vy)
1714 | {
1715 | return (ux*vx + uy*vy) / (nsvg__vmag(ux,uy) * nsvg__vmag(vx,vy));
1716 | }
1717 |
1718 | static float nsvg__vecang(float ux, float uy, float vx, float vy)
1719 | {
1720 | float r = nsvg__vecrat(ux,uy, vx,vy);
1721 | if (r < -1.0f) r = -1.0f;
1722 | if (r > 1.0f) r = 1.0f;
1723 | return ((ux*vy < uy*vx) ? -1.0f : 1.0f) * acosf(r);
1724 | }
1725 |
1726 | static void nsvg__pathArcTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel)
1727 | {
1728 | // Ported from canvg (https://code.google.com/p/canvg/)
1729 | float rx, ry, rotx;
1730 | float x1, y1, x2, y2, cx, cy, dx, dy, d;
1731 | float x1p, y1p, cxp, cyp, s, sa, sb;
1732 | float ux, uy, vx, vy, a1, da;
1733 | float x, y, tanx, tany, a, px, py, ptanx, ptany, t[6];
1734 | float sinrx, cosrx;
1735 | int fa, fs;
1736 | int i, ndivs;
1737 | float hda, kappa;
1738 |
1739 | rx = fabsf(args[0]); // y radius
1740 | ry = fabsf(args[1]); // x radius
1741 | rotx = args[2] / 180.0f * NSVG_PI; // x rotation engle
1742 | fa = fabsf(args[3]) > 1e-6 ? 1 : 0; // Large arc
1743 | fs = fabsf(args[4]) > 1e-6 ? 1 : 0; // Sweep direction
1744 | x1 = *cpx; // start point
1745 | y1 = *cpy;
1746 | if (rel) { // end point
1747 | x2 = *cpx + args[5];
1748 | y2 = *cpy + args[6];
1749 | } else {
1750 | x2 = args[5];
1751 | y2 = args[6];
1752 | }
1753 |
1754 | dx = x1 - x2;
1755 | dy = y1 - y2;
1756 | d = sqrtf(dx*dx + dy*dy);
1757 | if (d < 1e-6f || rx < 1e-6f || ry < 1e-6f) {
1758 | // The arc degenerates to a line
1759 | nsvg__lineTo(p, x2, y2);
1760 | *cpx = x2;
1761 | *cpy = y2;
1762 | return;
1763 | }
1764 |
1765 | sinrx = sinf(rotx);
1766 | cosrx = cosf(rotx);
1767 |
1768 | // Convert to center point parameterization.
1769 | // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
1770 | // 1) Compute x1', y1'
1771 | x1p = cosrx * dx / 2.0f + sinrx * dy / 2.0f;
1772 | y1p = -sinrx * dx / 2.0f + cosrx * dy / 2.0f;
1773 | d = nsvg__sqr(x1p)/nsvg__sqr(rx) + nsvg__sqr(y1p)/nsvg__sqr(ry);
1774 | if (d > 1) {
1775 | d = sqrtf(d);
1776 | rx *= d;
1777 | ry *= d;
1778 | }
1779 | // 2) Compute cx', cy'
1780 | s = 0.0f;
1781 | sa = nsvg__sqr(rx)*nsvg__sqr(ry) - nsvg__sqr(rx)*nsvg__sqr(y1p) - nsvg__sqr(ry)*nsvg__sqr(x1p);
1782 | sb = nsvg__sqr(rx)*nsvg__sqr(y1p) + nsvg__sqr(ry)*nsvg__sqr(x1p);
1783 | if (sa < 0.0f) sa = 0.0f;
1784 | if (sb > 0.0f)
1785 | s = sqrtf(sa / sb);
1786 | if (fa == fs)
1787 | s = -s;
1788 | cxp = s * rx * y1p / ry;
1789 | cyp = s * -ry * x1p / rx;
1790 |
1791 | // 3) Compute cx,cy from cx',cy'
1792 | cx = (x1 + x2)/2.0f + cosrx*cxp - sinrx*cyp;
1793 | cy = (y1 + y2)/2.0f + sinrx*cxp + cosrx*cyp;
1794 |
1795 | // 4) Calculate theta1, and delta theta.
1796 | ux = (x1p - cxp) / rx;
1797 | uy = (y1p - cyp) / ry;
1798 | vx = (-x1p - cxp) / rx;
1799 | vy = (-y1p - cyp) / ry;
1800 | a1 = nsvg__vecang(1.0f,0.0f, ux,uy); // Initial angle
1801 | da = nsvg__vecang(ux,uy, vx,vy); // Delta angle
1802 |
1803 | // if (vecrat(ux,uy,vx,vy) <= -1.0f) da = NSVG_PI;
1804 | // if (vecrat(ux,uy,vx,vy) >= 1.0f) da = 0;
1805 |
1806 | if (fa) {
1807 | // Choose large arc
1808 | if (da > 0.0f)
1809 | da = da - 2*NSVG_PI;
1810 | else
1811 | da = 2*NSVG_PI + da;
1812 | }
1813 |
1814 | // Approximate the arc using cubic spline segments.
1815 | t[0] = cosrx; t[1] = sinrx;
1816 | t[2] = -sinrx; t[3] = cosrx;
1817 | t[4] = cx; t[5] = cy;
1818 |
1819 | // Split arc into max 90 degree segments.
1820 | ndivs = (int)(fabsf(da) / (NSVG_PI*0.5f) + 0.5f);
1821 | hda = (da / (float)ndivs) / 2.0f;
1822 | kappa = fabsf(4.0f / 3.0f * (1.0f - cosf(hda)) / sinf(hda));
1823 | if (da < 0.0f)
1824 | kappa = -kappa;
1825 |
1826 | for (i = 0; i <= ndivs; i++) {
1827 | a = a1 + da * (i/(float)ndivs);
1828 | dx = cosf(a);
1829 | dy = sinf(a);
1830 | nsvg__xformPoint(&x, &y, dx*rx, dy*ry, t); // position
1831 | nsvg__xformVec(&tanx, &tany, -dy*rx * kappa, dx*ry * kappa, t); // tangent
1832 | if (i > 0)
1833 | nsvg__cubicBezTo(p, px+ptanx,py+ptany, x-tanx, y-tany, x, y);
1834 | px = x;
1835 | py = y;
1836 | ptanx = tanx;
1837 | ptany = tany;
1838 | }
1839 |
1840 | *cpx = x2;
1841 | *cpy = y2;
1842 | }
1843 |
1844 | static void nsvg__parsePath(NSVGparser* p, const char** attr)
1845 | {
1846 | const char* s = NULL;
1847 | char cmd;
1848 | float args[10];
1849 | int nargs;
1850 | int rargs;
1851 | float cpx, cpy, cpx2, cpy2;
1852 | const char* tmp[4];
1853 | char closedFlag;
1854 | int i;
1855 | char item[64];
1856 |
1857 | for (i = 0; attr[i]; i += 2) {
1858 | if (strcmp(attr[i], "d") == 0) {
1859 | s = attr[i + 1];
1860 | } else {
1861 | tmp[0] = attr[i];
1862 | tmp[1] = attr[i + 1];
1863 | tmp[2] = 0;
1864 | tmp[3] = 0;
1865 | nsvg__parseAttribs(p, tmp);
1866 | }
1867 | }
1868 |
1869 | if (s) {
1870 | nsvg__resetPath(p);
1871 | cpx = 0; cpy = 0;
1872 | closedFlag = 0;
1873 | nargs = 0;
1874 |
1875 | while (*s) {
1876 | s = nsvg__getNextPathItem(s, item);
1877 | if (!*item) break;
1878 | if (nsvg__isnum(item[0])) {
1879 | if (nargs < 10)
1880 | args[nargs++] = (float)atof(item);
1881 | if (nargs >= rargs) {
1882 | switch (cmd) {
1883 | case 'm':
1884 | case 'M':
1885 | nsvg__pathMoveTo(p, &cpx, &cpy, args, cmd == 'm' ? 1 : 0);
1886 | // Moveto can be followed by multiple coordinate pairs,
1887 | // which should be treated as linetos.
1888 | cmd = (cmd == 'm') ? 'l' : 'L';
1889 | rargs = nsvg__getArgsPerElement(cmd);
1890 | cpx2 = cpx; cpy2 = cpy;
1891 | break;
1892 | case 'l':
1893 | case 'L':
1894 | nsvg__pathLineTo(p, &cpx, &cpy, args, cmd == 'l' ? 1 : 0);
1895 | cpx2 = cpx; cpy2 = cpy;
1896 | break;
1897 | case 'H':
1898 | case 'h':
1899 | nsvg__pathHLineTo(p, &cpx, &cpy, args, cmd == 'h' ? 1 : 0);
1900 | cpx2 = cpx; cpy2 = cpy;
1901 | break;
1902 | case 'V':
1903 | case 'v':
1904 | nsvg__pathVLineTo(p, &cpx, &cpy, args, cmd == 'v' ? 1 : 0);
1905 | cpx2 = cpx; cpy2 = cpy;
1906 | break;
1907 | case 'C':
1908 | case 'c':
1909 | nsvg__pathCubicBezTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 'c' ? 1 : 0);
1910 | break;
1911 | case 'S':
1912 | case 's':
1913 | nsvg__pathCubicBezShortTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 's' ? 1 : 0);
1914 | break;
1915 | case 'Q':
1916 | case 'q':
1917 | nsvg__pathQuadBezTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 'q' ? 1 : 0);
1918 | break;
1919 | case 'T':
1920 | case 't':
1921 | nsvg__pathQuadBezShortTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 's' ? 1 : 0);
1922 | break;
1923 | case 'A':
1924 | case 'a':
1925 | nsvg__pathArcTo(p, &cpx, &cpy, args, cmd == 'a' ? 1 : 0);
1926 | cpx2 = cpx; cpy2 = cpy;
1927 | break;
1928 | default:
1929 | if (nargs >= 2) {
1930 | cpx = args[nargs-2];
1931 | cpy = args[nargs-1];
1932 | cpx2 = cpx; cpy2 = cpy;
1933 | }
1934 | break;
1935 | }
1936 | nargs = 0;
1937 | }
1938 | } else {
1939 | cmd = item[0];
1940 | rargs = nsvg__getArgsPerElement(cmd);
1941 | if (cmd == 'M' || cmd == 'm') {
1942 | // Commit path.
1943 | if (p->npts > 0)
1944 | nsvg__addPath(p, closedFlag);
1945 | // Start new subpath.
1946 | nsvg__resetPath(p);
1947 | closedFlag = 0;
1948 | nargs = 0;
1949 | } else if (cmd == 'Z' || cmd == 'z') {
1950 | closedFlag = 1;
1951 | // Commit path.
1952 | if (p->npts > 0) {
1953 | // Move current point to first point
1954 | cpx = p->pts[0];
1955 | cpy = p->pts[1];
1956 | cpx2 = cpx; cpy2 = cpy;
1957 | nsvg__addPath(p, closedFlag);
1958 | }
1959 | // Start new subpath.
1960 | nsvg__resetPath(p);
1961 | nsvg__moveTo(p, cpx, cpy);
1962 | closedFlag = 0;
1963 | nargs = 0;
1964 | }
1965 | }
1966 | }
1967 | // Commit path.
1968 | if (p->npts)
1969 | nsvg__addPath(p, closedFlag);
1970 | }
1971 |
1972 | nsvg__addShape(p);
1973 | }
1974 |
1975 | static void nsvg__parseRect(NSVGparser* p, const char** attr)
1976 | {
1977 | float x = 0.0f;
1978 | float y = 0.0f;
1979 | float w = 0.0f;
1980 | float h = 0.0f;
1981 | float rx = -1.0f; // marks not set
1982 | float ry = -1.0f;
1983 | int i;
1984 |
1985 | for (i = 0; attr[i]; i += 2) {
1986 | if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) {
1987 | if (strcmp(attr[i], "x") == 0) x = nsvg__parseFloat(p, attr[i+1], 0);
1988 | if (strcmp(attr[i], "y") == 0) y = nsvg__parseFloat(p, attr[i+1], 1);
1989 | if (strcmp(attr[i], "width") == 0) w = nsvg__parseFloat(p, attr[i+1], 0);
1990 | if (strcmp(attr[i], "height") == 0) h = nsvg__parseFloat(p, attr[i+1], 1);
1991 | if (strcmp(attr[i], "rx") == 0) rx = fabsf(nsvg__parseFloat(p, attr[i+1], 0));
1992 | if (strcmp(attr[i], "ry") == 0) ry = fabsf(nsvg__parseFloat(p, attr[i+1], 1));
1993 | }
1994 | }
1995 |
1996 | if (rx < 0.0f && ry > 0.0f) rx = ry;
1997 | if (ry < 0.0f && rx > 0.0f) ry = rx;
1998 | if (rx < 0.0f) rx = 0.0f;
1999 | if (ry < 0.0f) ry = 0.0f;
2000 | if (rx > w/2.0f) rx = w/2.0f;
2001 | if (ry > h/2.0f) ry = h/2.0f;
2002 |
2003 | if (w != 0.0f && h != 0.0f) {
2004 | nsvg__resetPath(p);
2005 |
2006 | if (rx < 0.00001f || ry < 0.0001f) {
2007 | nsvg__moveTo(p, x, y);
2008 | nsvg__lineTo(p, x+w, y);
2009 | nsvg__lineTo(p, x+w, y+h);
2010 | nsvg__lineTo(p, x, y+h);
2011 | } else {
2012 | // Rounded rectangle
2013 | nsvg__moveTo(p, x+rx, y);
2014 | nsvg__lineTo(p, x+w-rx, y);
2015 | nsvg__cubicBezTo(p, x+w-rx*(1-NSVG_KAPPA90), y, x+w, y+ry*(1-NSVG_KAPPA90), x+w, y+ry);
2016 | nsvg__lineTo(p, x+w, y+h-ry);
2017 | nsvg__cubicBezTo(p, x+w, y+h-ry*(1-NSVG_KAPPA90), x+w-rx*(1-NSVG_KAPPA90), y+h, x+w-rx, y+h);
2018 | nsvg__lineTo(p, x+rx, y+h);
2019 | nsvg__cubicBezTo(p, x+rx*(1-NSVG_KAPPA90), y+h, x, y+h-ry*(1-NSVG_KAPPA90), x, y+h-ry);
2020 | nsvg__lineTo(p, x, y+ry);
2021 | nsvg__cubicBezTo(p, x, y+ry*(1-NSVG_KAPPA90), x+rx*(1-NSVG_KAPPA90), y, x+rx, y);
2022 | }
2023 |
2024 | nsvg__addPath(p, 1);
2025 |
2026 | nsvg__addShape(p);
2027 | }
2028 | }
2029 |
2030 | static void nsvg__parseCircle(NSVGparser* p, const char** attr)
2031 | {
2032 | float cx = 0.0f;
2033 | float cy = 0.0f;
2034 | float r = 0.0f;
2035 | int i;
2036 |
2037 | for (i = 0; attr[i]; i += 2) {
2038 | if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) {
2039 | if (strcmp(attr[i], "cx") == 0) cx = nsvg__parseFloat(p, attr[i+1], 0);
2040 | if (strcmp(attr[i], "cy") == 0) cy = nsvg__parseFloat(p, attr[i+1], 1);
2041 | if (strcmp(attr[i], "r") == 0) r = fabsf(nsvg__parseFloat(p, attr[i+1], 2));
2042 | }
2043 | }
2044 |
2045 | if (r > 0.0f) {
2046 | nsvg__resetPath(p);
2047 |
2048 | nsvg__moveTo(p, cx+r, cy);
2049 | nsvg__cubicBezTo(p, cx+r, cy+r*NSVG_KAPPA90, cx+r*NSVG_KAPPA90, cy+r, cx, cy+r);
2050 | nsvg__cubicBezTo(p, cx-r*NSVG_KAPPA90, cy+r, cx-r, cy+r*NSVG_KAPPA90, cx-r, cy);
2051 | nsvg__cubicBezTo(p, cx-r, cy-r*NSVG_KAPPA90, cx-r*NSVG_KAPPA90, cy-r, cx, cy-r);
2052 | nsvg__cubicBezTo(p, cx+r*NSVG_KAPPA90, cy-r, cx+r, cy-r*NSVG_KAPPA90, cx+r, cy);
2053 |
2054 | nsvg__addPath(p, 1);
2055 |
2056 | nsvg__addShape(p);
2057 | }
2058 | }
2059 |
2060 | static void nsvg__parseEllipse(NSVGparser* p, const char** attr)
2061 | {
2062 | float cx = 0.0f;
2063 | float cy = 0.0f;
2064 | float rx = 0.0f;
2065 | float ry = 0.0f;
2066 | int i;
2067 |
2068 | for (i = 0; attr[i]; i += 2) {
2069 | if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) {
2070 | if (strcmp(attr[i], "cx") == 0) cx = nsvg__parseFloat(p, attr[i+1], 0);
2071 | if (strcmp(attr[i], "cy") == 0) cy = nsvg__parseFloat(p, attr[i+1], 1);
2072 | if (strcmp(attr[i], "rx") == 0) rx = fabsf(nsvg__parseFloat(p, attr[i+1], 0));
2073 | if (strcmp(attr[i], "ry") == 0) ry = fabsf(nsvg__parseFloat(p, attr[i+1], 1));
2074 | }
2075 | }
2076 |
2077 | if (rx > 0.0f && ry > 0.0f) {
2078 |
2079 | nsvg__resetPath(p);
2080 |
2081 | nsvg__moveTo(p, cx+rx, cy);
2082 | nsvg__cubicBezTo(p, cx+rx, cy+ry*NSVG_KAPPA90, cx+rx*NSVG_KAPPA90, cy+ry, cx, cy+ry);
2083 | nsvg__cubicBezTo(p, cx-rx*NSVG_KAPPA90, cy+ry, cx-rx, cy+ry*NSVG_KAPPA90, cx-rx, cy);
2084 | nsvg__cubicBezTo(p, cx-rx, cy-ry*NSVG_KAPPA90, cx-rx*NSVG_KAPPA90, cy-ry, cx, cy-ry);
2085 | nsvg__cubicBezTo(p, cx+rx*NSVG_KAPPA90, cy-ry, cx+rx, cy-ry*NSVG_KAPPA90, cx+rx, cy);
2086 |
2087 | nsvg__addPath(p, 1);
2088 |
2089 | nsvg__addShape(p);
2090 | }
2091 | }
2092 |
2093 | static void nsvg__parseLine(NSVGparser* p, const char** attr)
2094 | {
2095 | float x1 = 0.0;
2096 | float y1 = 0.0;
2097 | float x2 = 0.0;
2098 | float y2 = 0.0;
2099 | int i;
2100 |
2101 | for (i = 0; attr[i]; i += 2) {
2102 | if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) {
2103 | if (strcmp(attr[i], "x1") == 0) x1 = nsvg__parseFloat(p, attr[i + 1], 0);
2104 | if (strcmp(attr[i], "y1") == 0) y1 = nsvg__parseFloat(p, attr[i + 1], 1);
2105 | if (strcmp(attr[i], "x2") == 0) x2 = nsvg__parseFloat(p, attr[i + 1], 0);
2106 | if (strcmp(attr[i], "y2") == 0) y2 = nsvg__parseFloat(p, attr[i + 1], 1);
2107 | }
2108 | }
2109 |
2110 | nsvg__resetPath(p);
2111 |
2112 | nsvg__moveTo(p, x1, y1);
2113 | nsvg__lineTo(p, x2, y2);
2114 |
2115 | nsvg__addPath(p, 0);
2116 |
2117 | nsvg__addShape(p);
2118 | }
2119 |
2120 | static void nsvg__parsePoly(NSVGparser* p, const char** attr, int closeFlag)
2121 | {
2122 | int i;
2123 | const char* s;
2124 | float args[2];
2125 | int nargs, npts = 0;
2126 | char item[64];
2127 |
2128 | nsvg__resetPath(p);
2129 |
2130 | for (i = 0; attr[i]; i += 2) {
2131 | if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) {
2132 | if (strcmp(attr[i], "points") == 0) {
2133 | s = attr[i + 1];
2134 | nargs = 0;
2135 | while (*s) {
2136 | s = nsvg__getNextPathItem(s, item);
2137 | args[nargs++] = (float)atof(item);
2138 | if (nargs >= 2) {
2139 | if (npts == 0)
2140 | nsvg__moveTo(p, args[0], args[1]);
2141 | else
2142 | nsvg__lineTo(p, args[0], args[1]);
2143 | nargs = 0;
2144 | npts++;
2145 | }
2146 | }
2147 | }
2148 | }
2149 | }
2150 |
2151 | nsvg__addPath(p, (char)closeFlag);
2152 |
2153 | nsvg__addShape(p);
2154 | }
2155 |
2156 | static void nsvg__parseSVG(NSVGparser* p, const char** attr)
2157 | {
2158 | int i;
2159 | for (i = 0; attr[i]; i += 2) {
2160 | if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) {
2161 | if (strcmp(attr[i], "width") == 0) {
2162 | p->image->width = nsvg__parseFloat(p, attr[i + 1], 0);
2163 | } else if (strcmp(attr[i], "height") == 0) {
2164 | p->image->height = nsvg__parseFloat(p, attr[i + 1], 1);
2165 | } else if (strcmp(attr[i], "viewBox") == 0) {
2166 | sscanf(attr[i + 1], "%f%*[%%, \t]%f%*[%%, \t]%f%*[%%, \t]%f", &p->viewMinx, &p->viewMiny, &p->viewWidth, &p->viewHeight);
2167 | } else if (strcmp(attr[i], "preserveAspectRatio") == 0) {
2168 | if (strstr(attr[i + 1], "none") != 0) {
2169 | // No uniform scaling
2170 | p->alignType = NSVG_ALIGN_NONE;
2171 | } else {
2172 | // Parse X align
2173 | if (strstr(attr[i + 1], "xMin") != 0)
2174 | p->alignX = NSVG_ALIGN_MIN;
2175 | else if (strstr(attr[i + 1], "xMid") != 0)
2176 | p->alignX = NSVG_ALIGN_MID;
2177 | else if (strstr(attr[i + 1], "xMax") != 0)
2178 | p->alignX = NSVG_ALIGN_MAX;
2179 | // Parse X align
2180 | if (strstr(attr[i + 1], "yMin") != 0)
2181 | p->alignY = NSVG_ALIGN_MIN;
2182 | else if (strstr(attr[i + 1], "yMid") != 0)
2183 | p->alignY = NSVG_ALIGN_MID;
2184 | else if (strstr(attr[i + 1], "yMax") != 0)
2185 | p->alignY = NSVG_ALIGN_MAX;
2186 | // Parse meet/slice
2187 | p->alignType = NSVG_ALIGN_MEET;
2188 | if (strstr(attr[i + 1], "slice") != 0)
2189 | p->alignType = NSVG_ALIGN_SLICE;
2190 | }
2191 | }
2192 | }
2193 | }
2194 | }
2195 |
2196 | static void nsvg__parseGradient(NSVGparser* p, const char** attr, char type)
2197 | {
2198 | int i;
2199 | NSVGgradientData* grad = (NSVGgradientData*)malloc(sizeof(NSVGgradientData));
2200 | if (grad == NULL) return;
2201 | memset(grad, 0, sizeof(NSVGgradientData));
2202 |
2203 | grad->type = type;
2204 | nsvg__xformIdentity(grad->xform);
2205 |
2206 | // TODO: does not handle percent and objectBoundingBox correctly yet.
2207 | for (i = 0; attr[i]; i += 2) {
2208 | if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) {
2209 | if (strcmp(attr[i], "gradientUnits") == 0) {
2210 | if (strcmp(attr[i+1], "objectBoundingBox") == 0)
2211 | grad->units = NSVG_OBJECT_SPACE;
2212 | else
2213 | grad->units = NSVG_USER_SPACE;
2214 | } else if (strcmp(attr[i], "gradientTransform") == 0) {
2215 | nsvg__parseTransform(grad->xform, attr[i + 1]);
2216 | } else if (strcmp(attr[i], "cx") == 0) {
2217 | grad->radial.cx = nsvg__parseFloat(p, attr[i + 1], 0);
2218 | } else if (strcmp(attr[i], "cy") == 0) {
2219 | grad->radial.cy = nsvg__parseFloat(p, attr[i + 1], 1);
2220 | } else if (strcmp(attr[i], "r") == 0) {
2221 | grad->radial.r = nsvg__parseFloat(p, attr[i + 1], 2);
2222 | } else if (strcmp(attr[i], "fx") == 0) {
2223 | grad->radial.fx = nsvg__parseFloat(p, attr[i + 1], 0);
2224 | } else if (strcmp(attr[i], "fy") == 0) {
2225 | grad->radial.fy = nsvg__parseFloat(p, attr[i + 1], 1);
2226 | } else if (strcmp(attr[i], "x1") == 0) {
2227 | grad->linear.x1 = nsvg__parseFloat(p, attr[i + 1], 0);
2228 | } else if (strcmp(attr[i], "y1") == 0) {
2229 | grad->linear.y1 = nsvg__parseFloat(p, attr[i + 1], 1);
2230 | } else if (strcmp(attr[i], "x2") == 0) {
2231 | grad->linear.x2 = nsvg__parseFloat(p, attr[i + 1], 0);
2232 | } else if (strcmp(attr[i], "y2") == 0) {
2233 | grad->linear.y2 = nsvg__parseFloat(p, attr[i + 1], 1);
2234 | } else if (strcmp(attr[i], "spreadMethod") == 0) {
2235 | if (strcmp(attr[i+1], "pad") == 0)
2236 | grad->spread = NSVG_SPREAD_PAD;
2237 | else if (strcmp(attr[i+1], "reflect") == 0)
2238 | grad->spread = NSVG_SPREAD_REFLECT;
2239 | else if (strcmp(attr[i+1], "repeat") == 0)
2240 | grad->spread = NSVG_SPREAD_REPEAT;
2241 | } else if (strcmp(attr[i], "xlink:href") == 0) {
2242 | strncpy(grad->ref, attr[i+1], 63);
2243 | grad->ref[63] = '\0';
2244 | } else if (strcmp(attr[i], "id") == 0) {
2245 | strncpy(grad->id, attr[i+1], 63);
2246 | grad->id[63] = '\0';
2247 | }
2248 | }
2249 | }
2250 |
2251 | grad->next = p->gradients;
2252 | p->gradients = grad;
2253 | }
2254 |
2255 | static void nsvg__parseGradientStop(NSVGparser* p, const char** attr)
2256 | {
2257 | NSVGattrib* curAttr = nsvg__getAttr(p);
2258 | NSVGgradientData* grad;
2259 | NSVGgradientStop* stop;
2260 | int i, idx;
2261 |
2262 | curAttr->stopOffset = 0;
2263 | curAttr->stopColor = 0;
2264 | curAttr->stopOpacity = 1.0f;
2265 |
2266 | for (i = 0; attr[i]; i += 2) {
2267 | nsvg__parseAttr(p, attr[i], attr[i + 1]);
2268 | }
2269 |
2270 | // Add stop to the last gradient.
2271 | grad = p->gradients;
2272 | if (grad == NULL) return;
2273 |
2274 | grad->nstops++;
2275 | grad->stops = (NSVGgradientStop*)realloc(grad->stops, sizeof(NSVGgradientStop)*grad->nstops);
2276 | if (grad->stops == NULL) return;
2277 |
2278 | // Insert
2279 | idx = grad->nstops-1;
2280 | for (i = 0; i < grad->nstops-1; i++) {
2281 | if (curAttr->stopOffset < grad->stops[i].offset) {
2282 | idx = i;
2283 | break;
2284 | }
2285 | }
2286 | if (idx != grad->nstops-1) {
2287 | for (i = grad->nstops-1; i > idx; i--)
2288 | grad->stops[i] = grad->stops[i-1];
2289 | }
2290 |
2291 | stop = &grad->stops[idx];
2292 | stop->color = curAttr->stopColor;
2293 | stop->color |= (unsigned int)(curAttr->stopOpacity*255) << 24;
2294 | stop->offset = curAttr->stopOffset;
2295 | }
2296 |
2297 | static void nsvg__startElement(void* ud, const char* el, const char** attr)
2298 | {
2299 | NSVGparser* p = (NSVGparser*)ud;
2300 |
2301 | if (p->defsFlag) {
2302 | // Skip everything but gradients in defs
2303 | if (strcmp(el, "linearGradient") == 0) {
2304 | nsvg__parseGradient(p, attr, NSVG_PAINT_LINEAR_GRADIENT);
2305 | } else if (strcmp(el, "radialGradient") == 0) {
2306 | nsvg__parseGradient(p, attr, NSVG_PAINT_RADIAL_GRADIENT);
2307 | } else if (strcmp(el, "stop") == 0) {
2308 | nsvg__parseGradientStop(p, attr);
2309 | }
2310 | return;
2311 | }
2312 |
2313 | if (strcmp(el, "g") == 0) {
2314 | nsvg__pushAttr(p);
2315 | nsvg__parseAttribs(p, attr);
2316 | } else if (strcmp(el, "path") == 0) {
2317 | if (p->pathFlag) // Do not allow nested paths.
2318 | return;
2319 | nsvg__pushAttr(p);
2320 | nsvg__parsePath(p, attr);
2321 | nsvg__popAttr(p);
2322 | } else if (strcmp(el, "rect") == 0) {
2323 | nsvg__pushAttr(p);
2324 | nsvg__parseRect(p, attr);
2325 | nsvg__popAttr(p);
2326 | } else if (strcmp(el, "circle") == 0) {
2327 | nsvg__pushAttr(p);
2328 | nsvg__parseCircle(p, attr);
2329 | nsvg__popAttr(p);
2330 | } else if (strcmp(el, "ellipse") == 0) {
2331 | nsvg__pushAttr(p);
2332 | nsvg__parseEllipse(p, attr);
2333 | nsvg__popAttr(p);
2334 | } else if (strcmp(el, "line") == 0) {
2335 | nsvg__pushAttr(p);
2336 | nsvg__parseLine(p, attr);
2337 | nsvg__popAttr(p);
2338 | } else if (strcmp(el, "polyline") == 0) {
2339 | nsvg__pushAttr(p);
2340 | nsvg__parsePoly(p, attr, 0);
2341 | nsvg__popAttr(p);
2342 | } else if (strcmp(el, "polygon") == 0) {
2343 | nsvg__pushAttr(p);
2344 | nsvg__parsePoly(p, attr, 1);
2345 | nsvg__popAttr(p);
2346 | } else if (strcmp(el, "linearGradient") == 0) {
2347 | nsvg__parseGradient(p, attr, NSVG_PAINT_LINEAR_GRADIENT);
2348 | } else if (strcmp(el, "radialGradient") == 0) {
2349 | nsvg__parseGradient(p, attr, NSVG_PAINT_RADIAL_GRADIENT);
2350 | } else if (strcmp(el, "stop") == 0) {
2351 | nsvg__parseGradientStop(p, attr);
2352 | } else if (strcmp(el, "defs") == 0) {
2353 | p->defsFlag = 1;
2354 | } else if (strcmp(el, "svg") == 0) {
2355 | nsvg__parseSVG(p, attr);
2356 | }
2357 | }
2358 |
2359 | static void nsvg__endElement(void* ud, const char* el)
2360 | {
2361 | NSVGparser* p = (NSVGparser*)ud;
2362 |
2363 | if (strcmp(el, "g") == 0) {
2364 | nsvg__popAttr(p);
2365 | } else if (strcmp(el, "path") == 0) {
2366 | p->pathFlag = 0;
2367 | } else if (strcmp(el, "defs") == 0) {
2368 | p->defsFlag = 0;
2369 | }
2370 | }
2371 |
2372 | static void nsvg__content(void* ud, const char* s)
2373 | {
2374 | NSVG_NOTUSED(ud);
2375 | NSVG_NOTUSED(s);
2376 | // empty
2377 | }
2378 |
2379 | static void nsvg__imageBounds(NSVGparser* p, float* bounds)
2380 | {
2381 | NSVGshape* shape;
2382 | shape = p->image->shapes;
2383 | if (shape == NULL) return;
2384 | bounds[0] = shape->bounds[0];
2385 | bounds[1] = shape->bounds[1];
2386 | bounds[2] = shape->bounds[2];
2387 | bounds[3] = shape->bounds[3];
2388 | for (shape = shape->next; shape != NULL; shape = shape->next) {
2389 | bounds[0] = nsvg__minf(bounds[0], shape->bounds[0]);
2390 | bounds[1] = nsvg__minf(bounds[1], shape->bounds[1]);
2391 | bounds[2] = nsvg__maxf(bounds[2], shape->bounds[2]);
2392 | bounds[3] = nsvg__maxf(bounds[3], shape->bounds[3]);
2393 | }
2394 | }
2395 |
2396 | static float nsvg__viewAlign(float content, float container, int type)
2397 | {
2398 | if (type == NSVG_ALIGN_MIN)
2399 | return 0;
2400 | else if (type == NSVG_ALIGN_MAX)
2401 | return container - content;
2402 | // mid
2403 | return (container - content) * 0.5f;
2404 | }
2405 |
2406 | static void nsvg__scaleGradient(NSVGgradient* grad, float tx, float ty, float sx, float sy)
2407 | {
2408 | grad->xform[0] *= sx;
2409 | grad->xform[1] *= sx;
2410 | grad->xform[2] *= sy;
2411 | grad->xform[3] *= sy;
2412 | grad->xform[4] += tx*sx;
2413 | grad->xform[5] += ty*sx;
2414 | }
2415 |
2416 | static void nsvg__scaleToViewbox(NSVGparser* p, const char* units)
2417 | {
2418 | NSVGshape* shape;
2419 | NSVGpath* path;
2420 | float tx, ty, sx, sy, us, bounds[4], t[6];
2421 | int i;
2422 | float* pt;
2423 |
2424 | // Guess image size if not set completely.
2425 | nsvg__imageBounds(p, bounds);
2426 | if (p->viewWidth == 0) {
2427 | if (p->image->width > 0)
2428 | p->viewWidth = p->image->width;
2429 | else
2430 | p->viewWidth = bounds[2];
2431 | }
2432 | if (p->viewHeight == 0) {
2433 | if (p->image->height > 0)
2434 | p->viewHeight = p->image->height;
2435 | else
2436 | p->viewHeight = bounds[3];
2437 | }
2438 | if (p->image->width == 0)
2439 | p->image->width = p->viewWidth;
2440 | if (p->image->height == 0)
2441 | p->image->height = p->viewHeight;
2442 |
2443 | tx = -p->viewMinx;
2444 | ty = -p->viewMiny;
2445 | sx = p->viewWidth > 0 ? p->image->width / p->viewWidth : 0;
2446 | sy = p->viewHeight > 0 ? p->image->height / p->viewHeight : 0;
2447 | us = 1.0f / nsvg__convertToPixels(p, 1.0f, units, 0);
2448 |
2449 | // Fix aspect ratio
2450 | if (p->alignType == NSVG_ALIGN_MEET) {
2451 | // fit whole image into viewbox
2452 | sx = sy = nsvg__minf(sx, sy);
2453 | tx += nsvg__viewAlign(p->viewWidth*sx, p->image->width, p->alignX) / sx;
2454 | ty += nsvg__viewAlign(p->viewHeight*sy, p->image->height, p->alignY) / sy;
2455 | } else if (p->alignType == NSVG_ALIGN_SLICE) {
2456 | // fill whole viewbox with image
2457 | sx = sy = nsvg__maxf(sx, sy);
2458 | tx += nsvg__viewAlign(p->viewWidth*sx, p->image->width, p->alignX) / sx;
2459 | ty += nsvg__viewAlign(p->viewHeight*sy, p->image->height, p->alignY) / sy;
2460 | }
2461 |
2462 | // Transform
2463 | sx *= us;
2464 | sy *= us;
2465 | for (shape = p->image->shapes; shape != NULL; shape = shape->next) {
2466 | shape->bounds[0] = (shape->bounds[0] + tx) * sx;
2467 | shape->bounds[1] = (shape->bounds[1] + ty) * sy;
2468 | shape->bounds[2] = (shape->bounds[2] + tx) * sx;
2469 | shape->bounds[3] = (shape->bounds[3] + ty) * sy;
2470 | for (path = shape->paths; path != NULL; path = path->next) {
2471 | path->bounds[0] = (path->bounds[0] + tx) * sx;
2472 | path->bounds[1] = (path->bounds[1] + ty) * sy;
2473 | path->bounds[2] = (path->bounds[2] + tx) * sx;
2474 | path->bounds[3] = (path->bounds[3] + ty) * sy;
2475 | for (i =0; i < path->npts; i++) {
2476 | pt = &path->pts[i*2];
2477 | pt[0] = (pt[0] + tx) * sx;
2478 | pt[1] = (pt[1] + ty) * sy;
2479 | }
2480 | }
2481 |
2482 | if (shape->fill.type == NSVG_PAINT_LINEAR_GRADIENT || shape->fill.type == NSVG_PAINT_RADIAL_GRADIENT) {
2483 | nsvg__scaleGradient(shape->fill.gradient, tx,ty, sx,sy);
2484 | memcpy(t, shape->fill.gradient->xform, sizeof(float)*6);
2485 | nsvg__xformInverse(shape->fill.gradient->xform, t);
2486 | }
2487 | if (shape->stroke.type == NSVG_PAINT_LINEAR_GRADIENT || shape->stroke.type == NSVG_PAINT_RADIAL_GRADIENT) {
2488 | nsvg__scaleGradient(shape->stroke.gradient, tx,ty, sx,sy);
2489 | memcpy(t, shape->stroke.gradient->xform, sizeof(float)*6);
2490 | nsvg__xformInverse(shape->stroke.gradient->xform, t);
2491 | }
2492 |
2493 | }
2494 |
2495 | sx *= us;
2496 | sy *= us;
2497 | }
2498 |
2499 | NSVGimage* nsvgParse(char* input, const char* units, float dpi)
2500 | {
2501 | NSVGparser* p;
2502 | NSVGimage* ret = 0;
2503 |
2504 | p = nsvg__createParser();
2505 | if (p == NULL) {
2506 | return NULL;
2507 | }
2508 | p->dpi = dpi;
2509 |
2510 | nsvg__parseXML(input, nsvg__startElement, nsvg__endElement, nsvg__content, p);
2511 |
2512 | // Scale to viewBox
2513 | nsvg__scaleToViewbox(p, units);
2514 |
2515 | ret = p->image;
2516 | p->image = NULL;
2517 |
2518 | nsvg__deleteParser(p);
2519 |
2520 | return ret;
2521 | }
2522 |
2523 | NSVGimage* nsvgParseFromFile(const char* filename, const char* units, float dpi)
2524 | {
2525 | FILE* fp = NULL;
2526 | int size;
2527 | char* data = NULL;
2528 | NSVGimage* image = NULL;
2529 |
2530 | fp = fopen(filename, "rb");
2531 | if (!fp) goto error;
2532 | fseek(fp, 0, SEEK_END);
2533 | size = ftell(fp);
2534 | fseek(fp, 0, SEEK_SET);
2535 | data = (char*)malloc(size+1);
2536 | if (data == NULL) goto error;
2537 | fread(data, size, 1, fp);
2538 | data[size] = '\0'; // Must be null terminated.
2539 | fclose(fp);
2540 | image = nsvgParse(data, units, dpi);
2541 | free(data);
2542 |
2543 | return image;
2544 |
2545 | error:
2546 | if (fp) fclose(fp);
2547 | if (data) free(data);
2548 | if (image) nsvgDelete(image);
2549 | return NULL;
2550 | }
2551 |
2552 | void nsvgDelete(NSVGimage* image)
2553 | {
2554 | NSVGshape *snext, *shape;
2555 | if (image == NULL) return;
2556 | shape = image->shapes;
2557 | while (shape != NULL) {
2558 | snext = shape->next;
2559 | nsvg__deletePaths(shape->paths);
2560 | nsvg__deletePaint(&shape->fill);
2561 | nsvg__deletePaint(&shape->stroke);
2562 | free(shape);
2563 | shape = snext;
2564 | }
2565 | free(image);
2566 | }
2567 |
2568 | #endif
2569 |
--------------------------------------------------------------------------------
/svg2gcode.c:
--------------------------------------------------------------------------------
1 | /*
2 | * svg2gcode (c) Matti Koskinen 2014
3 | *
4 | * reorder-function based on StippleGen
5 | * Copyright (C) 2012 by Windell H. Oskay, www.evilmadscientist.com
6 | *
7 | * nanosvg.h (c) 2014 Mikko Mononen
8 | * some routines based on nanosvg-master example1.c
9 | *
10 | * Xgetopt used on VS2010 (or later) by Hans Dietrich,David Smith
11 | * code public domain
12 | *
13 | * No comments :-)
14 | *
15 | * This is free software; you can redistribute it and/or
16 | * modify it under the terms of the GNU Lesser General Public
17 | * License as published by the Free Software Foundation; either
18 | * version 2.1 of the License, or (at your option) any later version.
19 | *
20 | * http://creativecommons.org/licenses/LGPL/2.1/
21 | *
22 | * This library is distributed in the hope that it will be useful,
23 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
25 | * Lesser General Public License for more details.
26 | *
27 | * You should have received a copy of the GNU Lesser General Public
28 | * License along with this library; if not, write to the Free Software
29 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
30 | */
31 |
32 | #include
33 | #include
34 | #ifdef _MSC_VER
35 | #include "XGetopt.h"
36 | #else
37 | #include
38 | #endif
39 | #include
40 | #include
41 | #include
42 | #include
43 | #include
44 | //#define TESTRNG // remove if on linux or osx
45 | //#define DO_HPGL //remove comment if you want to get a HPGL-code
46 | #define NANOSVG_IMPLEMENTATION
47 | #include "nanosvg.h"
48 | #define GHEADER "G90\nG92 X0 Y0\n" //add here your specific G-codes
49 | #define GHEADER_NEW "nG90\nG92 X0 Y0\n" //add here your specific G-codes
50 | //separated with newline \n
51 | #define G32 1
52 | #ifdef G32
53 | #define CUTTERON "G0 M3 S%d\n"
54 | #else
55 | #define CUTTERON "M3 S%d\n" //I chose this, change to yours or add comment
56 | // or add newline "\n" if not needed
57 | #endif
58 | #define CUTTEROFF "M5\n" // same for this
59 | #define GFOOTER "M5\nG0 X0 Y0\n" //end G-code here
60 | #define GMODE "M4\n"
61 | //#define DO_HPGL //uncomment to get hpgl-file named test.hpgl on current folder
62 | static float minf(float a, float b) { return a < b ? a : b; }
63 | static float maxf(float a, float b) { return a > b ? a : b; }
64 | static float bounds[4];
65 | static int pathCount,pointsCount,shapeCount;
66 | static int doBez = 0;
67 | static int simplify = 0;
68 | static struct NSVGimage* g_image = NULL;
69 |
70 | typedef struct {
71 | float x;
72 | float y;
73 | } SVGPoint;
74 |
75 | typedef struct {
76 | float points[8];
77 | int city;
78 | char closed;
79 | } ToolPath;
80 |
81 | static SVGPoint bezPoints[64];
82 | static SVGPoint first,last;
83 | static int bezCount = 0;
84 | #ifdef _WIN32
85 | //typedef unsigned long int uint64_t;
86 | //typedef unsigned int uint32_t;
87 |
88 |
89 | static uint64_t seed;
90 |
91 | static int32_t rand31() {
92 | uint64_t tmp1;
93 | uint32_t tmp2;
94 |
95 | /* x = (16807 * x) % 0x7FFFFFFF */
96 | tmp1 = (uint64_t) ((int32_t) seed * (int64_t) 16807);
97 | tmp2 = (uint32_t) tmp1 & (uint32_t) 0x7FFFFFFF;
98 | tmp2 += (uint32_t) (tmp1 >> 31);
99 | if ((int32_t) tmp2 < (int32_t) 0)
100 | tmp2 = (tmp2 + (uint32_t) 1) & (uint32_t) 0x7FFFFFFF;
101 | return (int32_t)tmp2;
102 | }
103 | static void seedrand(float seedval) {
104 | seed = (int32_t) ((double) seedval + 0.5);
105 | if (seed < 1L) { /* seed from current time */
106 | seed = time(NULL);
107 | seed = ((seed - 1UL) % 0x7FFFFFFEUL) + 1UL;
108 | }
109 | else {
110 | seed = ((seed - 1L) % 0x7FFFFFFEL) + 1L;
111 | }
112 | seed = rand31(seed);
113 | seed = rand31(seed);
114 | }
115 |
116 | static double drnd31() {
117 | double x;
118 | seed = rand31();
119 | x = (double)(seed-0x3FFFFFFFL) * (2.0 / 1073741823.015625);
120 | if(fabs(x) > 1.0)
121 | x = drnd31();
122 | return fabs(x);
123 | }
124 | #endif
125 |
126 | static float distPtSeg(float x, float y, float px, float py, float qx, float qy)
127 | {
128 | float pqx, pqy, dx, dy, d, t;
129 | pqx = qx-px;
130 | pqy = qy-py;
131 | dx = x-px;
132 | dy = y-py;
133 | d = pqx*pqx + pqy*pqy;
134 | t = pqx*dx + pqy*dy;
135 | if (d > 0) t /= d;
136 | if (t < 0) t = 0;
137 | else if (t > 1) t = 1;
138 | dx = px + t*pqx - x;
139 | dy = py + t*pqy - y;
140 | return dx*dx + dy*dy;
141 | }
142 | // bezier smoothing
143 | static void cubicBez(float x1, float y1, float x2, float y2,
144 | float x3, float y3, float x4, float y4,
145 | float tol, int level)
146 | {
147 | float x12,y12,x23,y23,x34,y34,x123,y123,x234,y234,x1234,y1234;
148 | float d;
149 |
150 | if (level > 12) return;
151 |
152 | x12 = (x1+x2)*0.5f;
153 | y12 = (y1+y2)*0.5f;
154 | x23 = (x2+x3)*0.5f;
155 | y23 = (y2+y3)*0.5f;
156 | x34 = (x3+x4)*0.5f;
157 | y34 = (y3+y4)*0.5f;
158 | x123 = (x12+x23)*0.5f;
159 | y123 = (y12+y23)*0.5f;
160 | x234 = (x23+x34)*0.5f;
161 | y234 = (y23+y34)*0.5f;
162 | x1234 = (x123+x234)*0.5f;
163 | y1234 = (y123+y234)*0.5f;
164 |
165 | d = distPtSeg(x1234, y1234, x1,y1, x4,y4);
166 | if (d > tol*tol) {
167 | cubicBez(x1,y1, x12,y12, x123,y123, x1234,y1234, tol, level+1);
168 | cubicBez(x1234,y1234, x234,y234, x34,y34, x4,y4, tol, level+1);
169 | } else {
170 | bezPoints[bezCount].x = x4;
171 | bezPoints[bezCount].y = y4;
172 | bezCount++;
173 | if(bezCount > 63) {
174 | printf("!bez count\n");
175 | bezCount = 63;
176 | }
177 | }
178 | }
179 | //#define TESTRNG
180 | #ifdef _WIN32 //win doesn't have good RNG
181 | #define RANDOM() drnd31() //((double)rand()/(double)RAND_MAX)
182 | #else //OSX LINUX much faster than win
183 | #define RANDOM() (drand48())
184 | #endif
185 |
186 |
187 | static int pcomp(const void* a, const void* b) {
188 | SVGPoint* ap = (SVGPoint*)a;
189 | SVGPoint* bp = (SVGPoint*)b;
190 | if(sqrt(ap->x*ap->x + ap->y*ap->y) > sqrt(bp->x*bp->x+bp->y*bp->y))
191 | return 1;
192 | return -1;
193 | }
194 |
195 | // get all paths and add a city for each path
196 | static void calcPaths(SVGPoint* points, ToolPath* paths,int *cities, int *npaths) {
197 | struct NSVGshape* shape;
198 | struct NSVGpath* path;
199 | FILE *f;
200 | int i,j,k,l,p,b;
201 | SVGPoint* pts;
202 | #ifdef DO_HPGL
203 | f=fopen("test.hpgl","w");
204 | fprintf(f,"IN;SP1;");
205 | #endif
206 | i=0;
207 | k=0;
208 | j=0;
209 | p=0;
210 | for(shape = g_image->shapes; shape != NULL; shape=shape->next) {
211 | for(path = shape->paths; path != NULL; path=path->next) {
212 | if(path->closed && simplify) {
213 | pts = (SVGPoint*)malloc(path->npts*sizeof(SVGPoint));
214 |
215 | for(l=0;lnpts-1;l++) {
216 | float *pp=&path->pts[l*2];
217 | if(l==0) {
218 | points[i].x = pp[0];
219 | points[i].y = pp[1];
220 | }
221 | pts[l].x = pp[0];
222 | pts[l].y = pp[1];
223 | }
224 | qsort((void*)pts,path->npts-1,sizeof(SVGPoint),pcomp);
225 | paths[k].points[0] = pts[path->npts-2].x;
226 | paths[k].points[1] = pts[path->npts-2].y;
227 | paths[k].points[2] = pts[0].x;
228 | paths[k].points[3] = pts[0].y;
229 | paths[k].closed = path->closed;
230 | paths[k].city = i;
231 | k++;
232 | //fprintf(stderr,"i %d pts %f %f %f %f\n",i,pts[path->npts-2].x,pts[path->npts-2].y,pts[0].x,pts[0].y);
233 | free(pts);
234 |
235 | goto cont;
236 | }
237 |
238 | for(j=0;jnpts-1;(doBez ? j+=3 : j++)) {
239 | float *pp = &path->pts[j*2];
240 | if(j==0) {
241 |
242 | points[i].x = pp[0];
243 | points[i].y = pp[1];
244 | #ifdef DO_HPGL
245 | fprintf(f,"PU%d,%d;",(int)pp[0],(int)pp[1]);
246 | fflush(f);
247 |
248 | } else {
249 | fprintf(f,"PD%d,%d;",(int)pp[0],(int)pp[1]);
250 | fflush(f);
251 | #endif
252 | }
253 | if(doBez) {
254 | for(b=0;b<8;b++)
255 | paths[k].points[b]=pp[b];
256 | } else {
257 | paths[k].points[0] = pp[0];
258 | paths[k].points[1] = pp[1];
259 | paths[k].points[2] = pp[0];
260 | paths[k].points[3] = pp[1];
261 | }
262 | paths[k].closed = path->closed;
263 | paths[k].city = i;
264 | k++;
265 |
266 | }
267 | cont:
268 | if(k>pointsCount) {
269 | printf("error k > \n");
270 | #ifdef DO_HPGL
271 | fprintf(f,"PU0,0;\n");
272 | fclose(f);
273 | #endif
274 | *npaths = 0;
275 | return;
276 |
277 | }
278 | if(i>pathCount) {
279 | printf("error i > \n");
280 | #ifdef DO_HPGL
281 | fprintf(f,"PU0,0;\n");
282 | fclose(f);
283 | #endif
284 | exit(-1);
285 | /*
286 | *npaths=k;
287 | return;
288 | */
289 | }
290 | cities[i] = i;
291 | i++;
292 | }
293 |
294 | j++;
295 | }
296 | printf("total paths %d, total points %d\n",i,k);
297 | *npaths = k;
298 | #ifdef DO_HPGL
299 | fprintf(f,"PU0,0;\n");
300 | fclose(f);
301 | #endif
302 | }
303 |
304 | static void calcBounds(struct NSVGimage* image)
305 | {
306 | struct NSVGshape* shape;
307 | struct NSVGpath* path;
308 | int i;
309 | bounds[0] = FLT_MAX;
310 | bounds[1] = FLT_MAX;
311 | bounds[2] = -FLT_MAX;
312 | bounds[3] = -FLT_MAX;
313 | pathCount = 0;
314 | pointsCount = 0;
315 | shapeCount = 0;
316 | for (shape = image->shapes; shape != NULL; shape = shape->next) {
317 | for (path = shape->paths; path != NULL; path = path->next) {
318 | for (i = 0; i < path->npts-1; i++) {
319 | float* p = &path->pts[i*2];
320 | bounds[0] = minf(bounds[0], p[0]);
321 | bounds[1] = minf(bounds[1], p[1]);
322 | bounds[2] = maxf(bounds[2], p[0]);
323 | bounds[3] = maxf(bounds[3], p[1]);
324 | pointsCount++;
325 | }
326 | pathCount++;
327 | }
328 | shapeCount++;
329 | }
330 | printf("shapes %d\n",shapeCount);
331 | }
332 | //reorder the paths to minimize cutter movement
333 | static void reorder(SVGPoint* pts, int* cities, int ncity,char xy) {
334 | int i,j,k,temp1,temp2,indexA,indexB, indexH, indexL;
335 | float dx,dy,dist,dist2;
336 | SVGPoint p1,p2,p3,p4;
337 | for(i=0;i<800*ncity;i++) {
338 | indexA = (int)(RANDOM()*(ncity-2));
339 | indexB = (int)(RANDOM()*(ncity-2));
340 | if(abs(indexB-indexA) < 2)
341 | continue;
342 | if(indexB < indexA) {
343 | temp1 = indexB;
344 | indexB = indexA;
345 | indexA = temp1;
346 | }
347 | p1 = pts[cities[indexA]];
348 | p2 = pts[cities[indexA+1]];
349 | p3 = pts[cities[indexB]];
350 | p4 = pts[cities[indexB+1]];
351 | dx = p1.x-p2.x;
352 | dy = p1.y-p2.y;
353 | if(xy)
354 | dist = dx*dx+dy*dy;
355 | else
356 | dist = dy*dy;
357 | dx = p3.x-p4.x;
358 | dy = p3.y-p4.y;
359 | if(xy)
360 | dist += (dx*dx+dy*dy);
361 | else
362 | dist += dy*dy;
363 | dx = p1.x-p3.x;
364 | dy = p1.y-p3.y;
365 | if(xy)
366 | dist2 = dx*dx+dy*dy;
367 | else
368 | dist2 = dy*dy;
369 | dx = p2.x-p4.x;
370 | dy = p2.y-p4.y;
371 | if(xy)
372 | dist2 += (dx*dx+dy*dy);
373 | else
374 | dist2 += dy*dy;
375 | if(dist2 < dist) {
376 | indexH = indexB;
377 | indexL = indexA+1;
378 | while(indexH > indexL) {
379 | temp1 = cities[indexL];
380 | cities[indexL]=cities[indexH];
381 | cities[indexH] = temp1;
382 | indexH--;
383 | indexL++;
384 | }
385 | }
386 | }
387 | }
388 |
389 | void help() {
390 | printf("usage: svg2gcode [options] svg-file gcode-file\n");
391 | printf("options:\n");
392 | printf("\t-Y shift Y-ax\n");
393 | printf("\t-X sfit X-ax\n");
394 | printf("\t-c use z-axis instead of laser\n");
395 | printf("\t-f feed rate (3500)\n");
396 | printf("\t-n # number of reorders (30)\n");
397 | printf("\t-s scale (1.0)\n");
398 | printf("\t-F flip Y-axis\n");
399 | printf("\t-w final width in mm\n");
400 | printf("\t-t Bezier tolerance (0.5)\n");
401 | printf("\t-m machine accuracy (0.1)\n");
402 | printf("\t-z z-traverse (1.0)\n");
403 | printf("\t-Z z-engage (-1.0)\n");
404 | printf("\t-B do Bezier curve smoothing\n");
405 | printf("\t-T engrave only TSP-path\n");
406 | printf("\t-V optmize for Voronoi Stipples\n");
407 | printf("\t-h this help\n");
408 | }
409 |
410 | int main(int argc, char* argv[]) {
411 |
412 | int i,j,k,l,first = 1;
413 | //struct NSVGimage* image;
414 | struct NSVGshape *shape1,*shape2;
415 | struct NSVGpath *path1,*path2;
416 | SVGPoint* points;
417 | ToolPath* paths;
418 | int *cities,npaths;
419 | int feed = 3500;
420 | //int shiftY = 30;
421 | int fullspeed=4800;
422 | float ztraverse = 1.;
423 | float zengage = -1.;
424 | float width = -1;
425 | char xy = 1;
426 | float w,widthInmm = -1.;
427 | int numReord = 30;
428 | float scale = 1.;
429 | float tol = 0.5;
430 | float size = 100;
431 | float accuracy = 0.1;
432 | float x,y,bx,by,bxold,byold,d,firstx,firsty;
433 | float xold,yold;
434 | int flip = 0;
435 | int skip = 0;
436 | int units = 0;
437 | int printed=0;
438 | int cncMode = 0;
439 | int tsp = 0;
440 | int tspFirst = 1;
441 | int autoshift = 0;
442 | float ashift = 0.;
443 | int firstandonly = 2; // first svg
444 | int append = 0;
445 | int last = 0; // last svg
446 | int waitTime = 25;
447 | float maxx = -1000.,minx=1000.,maxy = -1000.,miny=1000.,zmax = -1000.,zmin = 1000;
448 | float shiftX = 0.;
449 | float shiftY = 0.;
450 | float zeroX = 0.;
451 | float zeroY = 0.;
452 | FILE *gcode;
453 | int pwr = 90;
454 | int ch;
455 | int dwell = -1;
456 | char gbuff[128];
457 | int center = 0;
458 | printf("v0.0001 8.11.2020\n");
459 | //seed48(NULL);
460 | if(argc < 3) {
461 | help();
462 | return -1;
463 | }
464 | simplify = 0;
465 | while((ch=getopt(argc,argv,"D:ABhf:n:s:Fz:Z:S:w:t:m:cTV1aLP:CY:X:")) != EOF) {
466 | switch(ch) {
467 | case 'C': center = 1;
468 | break;
469 | case 'P': pwr = atoi(optarg);
470 | break;
471 | case 'D': waitTime=atoi(optarg);
472 | dwell = atoi(optarg);
473 | break;
474 | case 'a': append = 1; firstandonly = 0;
475 | break;
476 | case 'V': xy = 0;
477 | break;
478 | case 'T': tsp = 1;
479 | if(xy == 0)
480 | xy = 1;
481 | break;
482 | case 'c': cncMode = 1;
483 | break;
484 |
485 | case 'Y': shiftY = atof(optarg); // shift
486 | break;
487 |
488 |
489 | case 'X': shiftX = atof(optarg); // shift
490 | break;
491 |
492 | case 'A':autoshift = 1;
493 | break;
494 | case 'm': accuracy=atof(optarg);
495 | break;
496 | case 'B': doBez = 1;
497 | break;
498 | case 'h': help();
499 | break;
500 | case 'f': feed = atoi(optarg);
501 | break;
502 | case 'n': numReord = atoi(optarg);
503 | break;
504 | case 's': scale = atof(optarg);
505 | break;
506 | case 't': tol = atof(optarg);
507 | break;
508 | case 'F': flip = 1;
509 | break;
510 | case 'z': ztraverse = atof(optarg);
511 | break;
512 | case 'Z': zengage = atof(optarg);
513 | break;
514 | case 'w': widthInmm = atof(optarg);
515 | break;
516 | default: help();
517 | return(1);
518 | break;
519 | }
520 | }
521 | if(shiftY != 30. && flip == 1)
522 | shiftY = -shiftY;
523 | g_image = nsvgParseFromFile(argv[optind],"px",96);
524 | if(g_image == NULL) {
525 | printf("error: Can't open input %s\n",argv[optind]);
526 | return -1;
527 | }
528 | calcBounds(g_image);
529 | fprintf(stderr,"bounds %f %f X %f %f\n",bounds[0],bounds[1],bounds[2],bounds[3]);
530 | width = g_image->width;
531 | fprintf(stderr,"width %f\n",width);
532 | w = fabs(bounds[0]-bounds[2]);
533 | if(widthInmm != -1.0)
534 | scale = widthInmm/w;
535 | fprintf(stderr,"width %f w %f scale %f width in mm %f\n",width,w,scale,widthInmm);
536 | zeroX = -bounds[0];
537 | zeroY = -bounds[1];
538 | /* old code
539 | if(bounds[0] < 0)
540 | zeroX = fabs(bounds[0]);
541 | if(bounds[2] < 0)
542 | zeroY = fabs(bounds[1]);
543 | //shiftY = shiftY+zeroY+scale*fabs(bounds[1]-bounds[3])/2.;
544 | //shiftX = shiftX+zeroX+scale*fabs(bounds[0]-bounds[2])/2.;
545 | // if(center == 0.) {
546 | // shiftX = 0.;
547 | // shiftY = 0.;
548 | //}
549 | */
550 |
551 | #ifdef _WIN32
552 | seedrand((float)time(0));
553 | #endif
554 | if(append)
555 | gcode = fopen(argv[optind+1],"a");
556 | else
557 | gcode=fopen(argv[optind+1],"w");
558 | if(gcode == NULL) {
559 | printf("can't open output %s\n",argv[optind+1]);
560 | return -1;
561 | }
562 | printf("paths %d points %d\n",pathCount, pointsCount);
563 | // allocate memory
564 | points = (SVGPoint*)malloc(pathCount*2*sizeof(SVGPoint));
565 | cities = (int*)malloc(pathCount*2*sizeof(int));
566 | paths = (ToolPath*)malloc(pointsCount*2*sizeof(ToolPath));
567 |
568 | npaths = 0;
569 | calcPaths(points,paths,cities,&npaths);
570 | /* if(doBez) {
571 | Bez = fopen("bez.lst","w");
572 | if(Bez == NULL) {
573 | printf("bez file!\n");
574 | return -1;
575 | }
576 | fprintf(Bez,"paths = %d\n",npaths);
577 | }
578 | */
579 | printf("reorder ");
580 | for(k=0;k= npaths-1) {
607 | //printf("k > \n");
608 | continue;
609 | }
610 | firstx = x = (paths[k].points[0]+zeroX)*scale+shiftX;
611 | firsty = y = (paths[k].points[1]+zeroY)*scale+shiftY; // changed
612 | if(flip) {
613 | firsty = -firsty;
614 | y = -y;
615 | }
616 | if(x > maxx)
617 | maxx = x;
618 | if(x < minx)
619 | minx = x;
620 | if(y > maxy)
621 | maxy = y;
622 | if(y < miny)
623 | miny = y;
624 | if(tsp) {
625 | if(tspFirst) {
626 | fprintf(gcode,"G1 X%.4f Y%.4f F4800 \n",x,y);
627 | fprintf(gcode,"G4 P0\n");
628 | tspFirst = 0;
629 | fprintf(gcode,CUTTERON,pwr);
630 | }
631 | else {
632 | fprintf(gcode,"G1 X%.1f Y%.1f F%d\n",x,y,feed);
633 | fprintf(gcode,"G4 P0\n");
634 | }
635 | continue;
636 | }
637 | if(!cncMode)
638 | fprintf(gcode,"G0 X%.4f Y%.4f\n",x,y);
639 | #ifndef G32
640 | else
641 | fprintf(gcode,"G0 X%.1f Y%.1f\n",x,y);
642 | fprintf(gcode,"G4 P0\n");
643 | #endif
644 | fprintf(gcode,"( city %d )\n",paths[k].city);
645 | #ifndef G32
646 | if(!cncMode)
647 | fprintf(gcode,CUTTERON,pwr);
648 | fprintf(gcode,"G4 P0\n");
649 | #endif
650 | printed=0;
651 | if(tsp)
652 | continue;
653 | for(j=k;j bounds[2] || bezPoints[l].x < bounds[0] || isnan(bezPoints[l].x)) {
670 | printf("bezPoints %f %f\n",bezPoints[l].x,bounds[0]);
671 | continue;
672 | }
673 | if(bezPoints[l].y > bounds[3]) {
674 | printf("bezPoints y %d\n",l);
675 | continue;
676 | }
677 | bx = (bezPoints[l].x+zeroX)*scale+shiftX;
678 | by = (bezPoints[l].y+zeroY)*scale+shiftY;
679 | if(flip)
680 | by = -by;
681 | if(bx > maxx)
682 | maxx = bx;
683 | if(bx < minx)
684 | minx = x;
685 | if(by > maxy)
686 | maxy = by;
687 | if(y < miny)
688 | miny = by;
689 |
690 | d = sqrt((bx-bxold)*(bx-bxold)+(by-byold)*(by-byold));
691 | if(1) {
692 | printed = 1;
693 | fprintf(gcode,"G1 X%.4f Y%.4f F%d\n",bx,by,feed);
694 | #ifndef G32
695 | fprintf(gcode,"G4 P0\n");
696 | #endif
697 | } else {
698 | fprintf(gcode,"( acc )\n");;//continue;
699 | //fprintf(gcode,"G05 P%d\n",(int)(pwr*0.33));
700 | fprintf(gcode,"G1 X%.4f Y%.4f F%d\n",bx,by,feed);
701 | }
702 | bxold = bx;
703 | byold = by;
704 | }
705 | } else {
706 | x = (paths[j].points[0]-fabs(bounds[0]))*scale+shiftX;
707 | y = (paths[j].points[1]-fabs(bounds[1]))*scale+shiftY;
708 | if(flip)
709 | y = -y;
710 | if(x > maxx)
711 | maxx = x;
712 | if(x < minx)
713 | minx = x;
714 | if(y > maxy)
715 | maxy = y;
716 | if(y < miny)
717 | miny = y;
718 |
719 | // if(paths[j].points[0]==paths[j].points[2] && paths[j].points[1]==paths[j].points[3]) {
720 | if(1) {
721 | //d = sqrt((x-xold)*(x-xold)+(y-yold)*(y-yold));
722 | if(1) {
723 | printed = 1;
724 | fprintf(gcode,"G1 X%.4f Y%.4f F%d\n",x,y,feed);
725 | #ifndef G32
726 | fprintf(gcode,"G4 P0\n");
727 | #endif
728 | } else {
729 | ;//continue;
730 | //fprintf(gcode,"G05 P%d\n",(int)(pwr*0.33));
731 | //fprintf(gcode,"G01 X%.4f Y%.4f F%d\n",x,y,feed);
732 | }
733 | first = 0;
734 | xold = x;
735 | yold = y;
736 | } else {
737 | x = (paths[j].points[0]-fabs(bounds[0]))*scale+shiftX;
738 | y = (paths[j].points[1]-fabs(bounds[1]))*scale+shiftY;
739 | if(flip)
740 | y = -y;
741 | fprintf(gcode,CUTTEROFF);
742 | fprintf(gcode,"( simplified )\n");
743 | fprintf(gcode,"G0 X%.4f Y%.4f\n",x,y);
744 | #ifndef G32
745 | fprintf(gcode,"G4 P0\n");
746 | #endif
747 | x = (paths[j].points[2]-fabs(bounds[0]))*scale+shiftX;
748 | y = (paths[j].points[3]-fabs(bounds[1]))*scale+shiftY;
749 | if(flip)
750 | y = -y;
751 | #ifndef G32
752 | fprintf(gcode,CUTTERON,pwr);
753 | #endif
754 | //fprintf(gcode,"G01 X%.4f Y%.4f F%d\n",x,y,feed);
755 | xold = x;
756 | yold = y;
757 | }
758 | }
759 | paths[j].city = -1;
760 | /*
761 | if(printed == 0) {
762 | if(doBez)
763 | fprintf(gcode,"G01 X%.4f Y%.4f F%d\n",bx,by,feed);
764 | else
765 | fprintf(gcode,"G01 X%.4f Y%.4f F%d\n",x,y,feed);
766 | }
767 | */
768 | } else
769 | break;
770 | }
771 | if(tsp)
772 | continue;
773 | if(paths[j].closed) {
774 | fprintf(gcode, "( end )\n");
775 | //bx = (bezPoints[0].x-bounds[0])*scale;
776 | //by = (flip ? (bounds[3]-bezPoints[0].y)*scale : (bezPoints[0].y-bounds[1])*scale);
777 |
778 | fprintf(gcode,"G1 X%.4f Y%.4f F%d\n",firstx,firsty,feed);
779 | #ifndef G32
780 | fprintf(gcode,"G4 P0\n");
781 | #endif
782 | printed = 1;
783 | }
784 | if(!cncMode) {
785 | if(!printed) {
786 | #ifndef G32
787 | if(dwell != -1) {
788 | fprintf(gcode,"( lowpwr )\n");
789 | fprintf(gcode,"M3 S10\n");
790 | sprintf(gbuff,"G4 P%d\n",dwell);
791 | fprintf(gcode,"%s",gbuff);
792 | }
793 | fprintf(gcode,"M5\n");
794 | #else
795 | fprintf(gcode,"( new dwell? )\n");
796 | #endif
797 | //fprintf(gcode,"G05 P%d\n",pwr);
798 | }
799 | #ifndef G32
800 | fprintf(gcode,CUTTEROFF);
801 | #endif
802 | } else {
803 | fprintf(gcode,"G1 ZF%d\n",feed);
804 | fprintf(gcode,"G4 P0\n");
805 | printed = 0;
806 | }
807 | }
808 | #ifndef G32
809 | if(tsp)
810 | fprintf(gcode,CUTTEROFF);
811 | #else
812 | fprintf(gcode,"M5\n");
813 | #endif
814 | fprintf(gcode,GFOOTER);
815 | printf("( size X%.4f Y%.4f x X%.4f Y%.4f )\n",minx,miny,maxx,maxy);
816 | fprintf(gcode,"( size X%.4f Y%.4f x X%.4f Y%.4f )\n",minx,miny,maxx,maxy);
817 | fclose(gcode);
818 | free(points);
819 | free(cities);
820 | free(paths);
821 | nsvgDelete(g_image);
822 | return 0;
823 | }
824 |
--------------------------------------------------------------------------------