1 /* Invisible Vector Library
2 * ported by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
3 * Understanding is not required. Only obedience.
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, version 3 of the License ONLY.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 // by technosaurus, https://github.com/memononen/nanosvg/issues/87#issue-231530483
18 module iv
.nanovega
.simplefont
;
21 import iv
.nanovega
.nanovega
;
25 // ////////////////////////////////////////////////////////////////////////// //
26 static immutable string
[96] simpleChars
= [
28 /* "!" */ "m4 1v9m0 1v1m4-12",
29 /*"\"" */ "m3 2v3m2-3v3m3-5",
30 /* "#" */ "m2 3v8m4-8v8m-5-2h6m-6-4h6m1-5",
31 /* "$" */ "m2 11a2 3 0 1 0 2-4a2 3 0 1 1 2-4m-2-2v12m4-15",
32 /* "%" */ "m2 3a1 1 0 1 1 0 .1zm4-2l-4 11m2-2a1 1 0 1 1 0 .1zm2-14",
33 /* "&" */ "m7 12l-3-6a2 2.5 0 1 1 .1 0a3 3 0 1 0 3 2m1-8",
34 /* "'" */ "m4 2v3m4-5",
35 /* "(" */ "m4 12q-3-6 0-11m4-1",
36 /* ")" */ "m4 12q3-6 0-11m4-1",
37 /* "*" */ "m1 6h6m-1 4l-4-8m2 0v8m-2 0l4-8m2-2",
38 /* "+" */ "m1 6h6m-3-3v6m4-9",
39 /* "," */ "m5 11q1 1-2 3m5-14",
40 /* "-" */ "m2 6h4m2-6",
41 /* "." */ "m3 12a.5.5 0 0 0-.1-.1zm5-12",
42 /* "/" */ "m2 12l4-11m2-1",
43 /* "0" */ "m2 5.5a2 4 0 1 1 4 0v2a2 4 0 1 1-4 0zm2 0v2m4-8",
44 /* "1" */ "m3 3l1-1v10m4-12",
45 /* "2" */ "m2 3a2 5 30 0 1 0 9h5m1-12",
46 /* "3" */ "m2 2a2 2 0 1 1 3 4m0 0h-1a3 3 0 1 1-3 4m7-10",
47 /* "4" */ "m2 1v5h4v6-11m2-1",
48 /* "5" */ "m2 10a2 4 0 1 0 0-4v-5h4m2-1",
49 /* "6" */ "m3 6a2 3 0 1 1 -1 1l4-6m2-1",
50 /* "7" */ "m1 1h5l-4 11m6-12",
51 /* "8" */ "m3.5 6a2 2.5 0 1 1 1 0a2 3 0 1 1 -1 0zm4.5-6",
52 /* "9" */ "m2 12l4-6a2.5 3 0 1 0 -1 1m3-7",
53 /* ":" */ "m3 3a.5.5 0 1 0 0-.1zm0 6a.5.5 0 1 0 0-.1zm5-9",
54 /* ";" */ "m3 3a.5.5 0 1 0 0-.1zm0 6a.5.5 0 1 0 0-.1zq2 0 -1 2m6-11",
55 /* "<" */ "m6 12l-4-6 4-5m2-1",
56 /* "=" */ "m2 5h4m-4 2h4m2-7",
57 /* ">" */ "m2 12l4-6-4-5m6-1",
58 /* "?" */ "m2 4a2 2 0 1 1 2 2v2m0 3a.5.5 0 1 0-.1 0zm4-11",
59 /* "@" */ "m5 8a1 1 0 1 1 0 -1a1 2 0 1 0 2-0a3 5 0 1 0 -2 4m3-11",
60 /* "A" */ "m1 12l3-10l3 10m-1-4h-4m6-8",
61 /* "B" */ "m2 1h1a2 2 0 1 1 0 5h-1h2a2 2 0 1 1 -2 6zm6-1",
62 /* "C" */ "m5.5 3a2 5 0 1 0 0 7m2.5-10",
63 /* "D" */ "m2 1h1a3 5 0 1 1 0 11h-1zm6-1",
64 /* "E" */ "m6 12h-4v-11h4m-4 5h3m3-6",
65 /* "F" */ "m2 12v-11h4m-4 5h3m3-6",
66 /* "G" */ "m6 4a2 5 0 1 0 0 5v-2h-2m4-7",
67 /* "H" */ "m2 1v11-6h4v6-11m2-1",
68 /* "I" */ "m2 1h4-2v11h2-4m6-12",
69 /* "J" */ "m4 1h2v7q0 4-4 4m6-12",
70 /* "K" */ "m2 1v11-6l5-5-4 4 4 7m1-12",
71 /* "L" */ "m2 1v11h4m2-12",
72 /* "M" */ "m1 12v-11l3 6 3-6v11m1-12",
73 /* "N" */ "m2 12v-11l4 11v-11m2-1",
74 /* "O" */ "m4 1a2 5.5 0 1 0 .1 0zm4-1",
75 /* "P" */ "m2 12v-11h1a3 2 0 1 1 0 5h-1m6-6",
76 /* "Q" */ "m4 1a2 5.5 0 1 0 .1 0zm0 7l3 4m1-12",
77 /* "R" */ "m2 12v-11h1a3 2 0 1 1 0 5h-1 1l4 6m1-12",
78 /* "S" */ "m2 10a2 3 0 1 0 2-4a2 2.5 0 1 1 2-4m2-2",
79 /* "T" */ "m1 1h6-3v11m4-12",
80 /* "U" */ "m2 1v9a2 2 0 0 0 4 0v-9m2-1",
81 /* "V" */ "m2 1l2 11 2-11m2-1",
82 /* "W" */ "m1 1l2 11 1-6 1 6 2-11m1-1",
83 /* "X" */ "m2 1l4 11m-4 0l4-11m2-1",
84 /* "Y" */ "m2 1l2 6v5-5l2-6m2-1",
85 /* "Z" */ "m6 12h-4l4-11h-4m6-1",
86 /* "[" */ "m5 12h-2v-11h2m3-1",
87 /*"\\" */ "m2 1l4 11m2-12",
88 /* "]" */ "m3 12h2v-11h-2m5-1",
89 /* "^" */ "m2 4l2-2 2 2m2-4",
90 /* "_" */ "m1 14h6m1-14",
91 /* "`" */ "m3 2l2 1m3-3",
92 /* "a" */ "m6 7a2 3 0 1 0 0 3v2-7m2-5",
93 /* "b" */ "m2 7a2 3 0 1 1 0 3v2-11m6-1",
94 /* "c" */ "m6 7a2 3 0 1 0 0 3m2-10",
95 /* "d" */ "m6 7a2 3 0 1 0 0 3v2-11m2-1",
96 /* "e" */ "m6 10a2 3 0 1 1 0-2h-4m6-8",
97 /* "f" */ "m2 12v-6h2-2q0-4 4-4m2-2",
98 /* "g" */ "m6 7a2 3 0 1 0 0 3v-5 8a2 2 0 0 1 -4 0m6-13",
99 /* "h" */ "m2 1v11-4a2 2 0 1 1 4 0v4m2-12",
100 /* "i" */ "m4 4v1m0 1v6m4-12",
101 /* "j" */ "m4 4v1m0 1v5q0 2-2 2m6-13",
102 /* "k" */ "m2 1v11-3l3 -3-2 2 3 4m2-12",
103 /* "l" */ "m4 1v11m4-12",
104 /* "m" */ "m2 6v6-4a1 2 0 1 1 2 0v4-4a1 2 0 1 1 2 0v4m2-12",
105 /* "n" */ "m2 6v6-4a2 2 0 1 1 4 0v4m2-12",
106 /* "o" */ "m6 9a2 3 0 1 0 0 .1zm2-9",
107 /* "p" */ "m2 7a2 3 0 1 1 0 3v5-10m6-5",
108 /* "q" */ "m6 7a2 3 0 1 0 0 3v5-10m2-5",
109 /* "r" */ "m2 6v6-4a2 2 0 1 1 4 0m2-8",
110 /* "s" */ "m2 10a2 2 0 1 0 2-2h-1a2 1 0 0 1 3-2m2-6",
111 /* "t" */ "m4 4v8-6h2-4m6-6",
112 /* "u" */ "m2 6v4a2 2 0 1 0 4 0v2-6m2-6",
113 /* "v" */ "m2 6l2 5 2-5m2-6",
114 /* "w" */ "m2 6l1 6 1-4 1 4 1-6m2-6",
115 /* "x" */ "m2 6l4 6m-4 0l4-6m2-6",
116 /* "y" */ "m2 6l2 4l2-4-4 8m6-14",
117 /* "z" */ "m2 6h4l-4 6h4m2-12",
118 /* "{" */ "m5 13a1 6 0 0 1-1-5l-1-1 1-1a1 6 0 0 1 1-5m3-1",
119 /* "|" */ "m4 13v-12m4-1",
120 /* "}" */ "m3 13a1 6 0 0 0 1-5l1-1-1-1a1 6 0 0 0 -1-5m5-1",
121 /* "~" */ "m2 6q1-1 2 0t2 0m2-6",
122 /* ??? */ "m1 1h6v14h-6zm7-1",
126 // ////////////////////////////////////////////////////////////////////////// //
128 public float simpleCharHeight (const(float)[] xform
=null) nothrow @trusted @nogc {
130 if (xform
.length
< 6) xf
[] = nvgIdentity
[]; else xf
[] = xform
[0..6];
131 float cx
= 8, cy
= 14;
132 nvgTransformPoint(cx
, cy
, xf
[]);
136 public float simpleCharWidth (const(float)[] xform
=null) nothrow @trusted @nogc {
138 if (xform
.length
< 6) xf
[] = nvgIdentity
[]; else xf
[] = xform
[0..6];
139 float cx
= 8, cy
= 14;
140 nvgTransformPoint(cx
, cy
, xf
[]);
145 public float drawSimpleChar (NVGContext ctx
, float x0
, float y0
, char ch
, const(float)[] xform
=null) nothrow @trusted @nogc {
147 if (xform
.length
< 6) xf
[] = nvgIdentity
[]; else xf
[] = xform
[0..6];
149 float firstx
= 0, firsty
= 0;
150 bool firstPoint
= true;
152 static float nsvg__sqr() (in float x
) { pragma(inline
, true); return x
*x
; }
153 static float nsvg__vmag() (in float x
, float y
) { pragma(inline
, true); import std
.math
: sqrt
; return sqrt(x
*x
+y
*y
); }
155 static float nsvg__vecrat (float ux
, float uy
, float vx
, float vy
) {
156 pragma(inline
, true);
157 return (ux
*vx
+uy
*vy
)/(nsvg__vmag(ux
, uy
)*nsvg__vmag(vx
, vy
));
160 static float nsvg__vecang (float ux
, float uy
, float vx
, float vy
) {
161 import std
.math
: acos
;
162 float r
= nsvg__vecrat(ux
, uy
, vx
, vy
);
163 if (r
< -1.0f) r
= -1.0f;
164 if (r
> 1.0f) r
= 1.0f;
165 return (ux
*vy
< uy
*vx ?
-1.0f : 1.0f)*acos(r
);
168 static void nsvg__xformPoint (float* dx
, float* dy
, in float x
, in float y
, const(float)* t
) {
169 if (dx
!is null) *dx
= x
*t
[0]+y
*t
[2]+t
[4];
170 if (dy
!is null) *dy
= x
*t
[1]+y
*t
[3]+t
[5];
173 static void nsvg__xformVec (float* dx
, float* dy
, in float x
, in float y
, const(float)* t
) {
174 if (dx
!is null) *dx
= x
*t
[0]+y
*t
[2];
175 if (dy
!is null) *dy
= x
*t
[1]+y
*t
[3];
178 // Ported from canvg (https://code.google.com/p/canvg/)
179 void nsvg__pathArcTo (float* cpx
, float* cpy
, const(float)[] args
, bool relative
) {
180 enum NSVG_PI
= 3.14159265358979323846264338327f;
181 import std
.math
: fabsf
= abs
, cosf
= cos
, sinf
= sin
, sqrtf
= sqrt
;
182 assert(args
.length
>= 7);
184 float px
= 0, py
= 0, ptanx
= 0, ptany
= 0;
186 float x2
= args
[5], y2
= args
[6]; // end point
188 float rx
= fabsf(args
[0]); // y radius
189 float ry
= fabsf(args
[1]); // x radius
190 immutable float rotx
= args
[2]/180.0f*NSVG_PI
; // x rotation engle
191 immutable float fa
= (fabsf(args
[3]) > 1e-6 ?
1 : 0); // Large arc
192 immutable float fs
= (fabsf(args
[4]) > 1e-6 ?
1 : 0); // Sweep direction
193 immutable float x1
= *cpx
; // start point
194 immutable float y1
= *cpy
;
203 float d
= sqrtf(dx
*dx
+dy
*dy
);
204 if (d
< 1e-6f || rx
< 1e-6f || ry
< 1e-6f) {
205 // The arc degenerates to a line
206 if (firstPoint
) { firstx
= x2
; firsty
= y2
; firstPoint
= false; }
208 nvgTransformPoint(&vgx
, &vgy
, xf
[], x2
, y2
);
209 ctx
.lineTo(x0
+vgx
, y0
+vgy
);
210 //nsvg__lineTo(p, x2, y2);
216 immutable float sinrx
= sinf(rotx
);
217 immutable float cosrx
= cosf(rotx
);
219 // Convert to center point parameterization.
220 // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
221 // 1) Compute x1', y1'
222 immutable float x1p
= cosrx
*dx
/2.0f+sinrx
*dy
/2.0f;
223 immutable float y1p
= -sinrx
*dx
/2.0f+cosrx
*dy
/2.0f;
224 d
= nsvg__sqr(x1p
)/nsvg__sqr(rx
)+nsvg__sqr(y1p
)/nsvg__sqr(ry
);
230 // 2) Compute cx', cy'
232 float sa
= nsvg__sqr(rx
)*nsvg__sqr(ry
)-nsvg__sqr(rx
)*nsvg__sqr(y1p
)-nsvg__sqr(ry
)*nsvg__sqr(x1p
);
233 immutable float sb
= nsvg__sqr(rx
)*nsvg__sqr(y1p
)+nsvg__sqr(ry
)*nsvg__sqr(x1p
);
234 if (sa
< 0.0f) sa
= 0.0f;
235 if (sb
> 0.0f) s
= sqrtf(sa
/sb
);
236 if (fa
== fs
) s
= -s
;
237 immutable float cxp
= s
*rx
*y1p
/ry
;
238 immutable float cyp
= s
*-ry
*x1p
/rx
;
240 // 3) Compute cx,cy from cx',cy'
241 immutable float cx
= (x1
+x2
)/2.0f+cosrx
*cxp
-sinrx
*cyp
;
242 immutable float cy
= (y1
+y2
)/2.0f+sinrx
*cxp
+cosrx
*cyp
;
244 // 4) Calculate theta1, and delta theta.
245 immutable float ux
= (x1p
-cxp
)/rx
;
246 immutable float uy
= (y1p
-cyp
)/ry
;
247 immutable float vx
= (-x1p
-cxp
)/rx
;
248 immutable float vy
= (-y1p
-cyp
)/ry
;
249 immutable float a1
= nsvg__vecang(1.0f, 0.0f, ux
, uy
); // Initial angle
250 float da = nsvg__vecang(ux
, uy
, vx
, vy
); // Delta angle
252 if (fs
== 0 && da > 0) da -= 2*NSVG_PI
;
253 else if (fs
== 1 && da < 0) da += 2*NSVG_PI
;
255 // Approximate the arc using cubic spline segments.
256 t
[0] = cosrx
; t
[1] = sinrx
;
257 t
[2] = -sinrx
; t
[3] = cosrx
;
258 t
[4] = cx
; t
[5] = cy
;
260 // Split arc into max 90 degree segments.
261 // The loop assumes an iteration per end point (including start and end), this +1.
262 immutable ndivs
= cast(int)(fabsf(da)/(NSVG_PI
*0.5f)+1.0f);
263 immutable float hda
= (da/cast(float)ndivs
)/2.0f;
264 float kappa
= fabsf(4.0f/3.0f*(1.0f-cosf(hda
))/sinf(hda
));
265 if (da < 0.0f) kappa
= -kappa
;
267 immutable float ndivsf
= cast(float)ndivs
;
268 foreach (int i
; 0..ndivs
+1) {
269 float x
= void, y
= void, tanx
= void, tany
= void;
270 immutable float a
= a1
+da*(i
/ndivsf
);
273 nsvg__xformPoint(&x
, &y
, dx
*rx
, dy
*ry
, t
.ptr
); // position
274 nsvg__xformVec(&tanx
, &tany
, -dy
*rx
*kappa
, dx
*ry
*kappa
, t
.ptr
); // tangent
276 if (firstPoint
) { firstx
= px
+ptanx
; firsty
= py
+ptany
; firstPoint
= false; }
277 float vgx1
, vgy1
, vgx2
, vgy2
, vgx3
, vgy3
;
278 nvgTransformPoint(&vgx1
, &vgy1
, xf
[], px
+ptanx
, py
+ptany
);
279 nvgTransformPoint(&vgx2
, &vgy2
, xf
[], x
-tanx
, y
-tany
);
280 nvgTransformPoint(&vgx3
, &vgy3
, xf
[], x
, y
);
281 ctx
.bezierTo(x0
+vgx1
, y0
+vgy1
, x0
+vgx2
, y0
+vgy2
, x0
+vgx3
, y0
+vgy3
);
282 //nsvg__cubicBezTo(p, px+ptanx, py+ptany, x-tanx, y-tany, x, y);
294 if (ch
< ' ' || ch
>= 0x80) {
296 nvgTransformPoint(x
, y
, xf
[]);
299 string cmd
= simpleChars
[cast(int)ch
-32];
300 float[8] args
= void;
301 float cx
= 0, cy
= 0;
303 enum Code
{ MoveTo
, LineTo
, HorizTo
, VertTo
, ArcTo
, QuadTo
, ShortQuadTo
, Close
}
304 Code code
= Code
.Close
;
305 int argc
= 0, argn
= 0;
308 if (cmd
[0] <= ' ' || cmd
[0] == ',') { cmd
= cmd
[1..$]; continue; }
310 case 'm': code
= Code
.MoveTo
; cmd
= cmd
[1..$]; argc
= 2; argn
= 0; break;
311 case 'l': code
= Code
.LineTo
; cmd
= cmd
[1..$]; argc
= 2; argn
= 0; break;
312 case 'a': code
= Code
.ArcTo
; cmd
= cmd
[1..$]; argc
= 7; argn
= 0; break;
313 case 'h': code
= Code
.HorizTo
; cmd
= cmd
[1..$]; argc
= 1; argn
= 0; break;
314 case 'v': code
= Code
.VertTo
; cmd
= cmd
[1..$]; argc
= 1; argn
= 0; break;
315 case 'q': code
= Code
.QuadTo
; cmd
= cmd
[1..$]; argc
= 4; argn
= 0; break;
316 case 't': code
= Code
.ShortQuadTo
; cmd
= cmd
[1..$]; argc
= 2; argn
= 0; break;
320 nvgTransformPoint(&vgx
, &vgy
, xf
[], firstx
, firsty
);
321 //conwriteln("Z: cx=", cx, "; cy=", cy, "; fx=", firstx, "; fy=", firsty);
322 ctx
.lineTo(x0
+vgx
, y0
+vgy
);
330 import std
.math
: isNaN
;
331 if (cmd
[0].isalpha
) assert(0, "unknown command"); //: '"~cmd[0]~"'");
333 if (cmd
[0] == '+' || cmd
[0] == '-') ++end
;
334 while (end
< cmd
.length
&& cmd
[end
].isdigit
) ++end
;
335 if (end
< cmd
.length
&& cmd
[end
] == '.') {
337 while (end
< cmd
.length
&& cmd
[end
].isdigit
) ++end
;
339 auto arg
= atof(cmd
[0..end
]);
340 //static assert(is(typeof(arg) == float));
341 if (arg
.isNaN
) assert(0, "ooops"); //: <"~cmd[0..end].idup~"> : "~cmd.idup);
344 assert(argn
<= argc
);
347 final switch (code
) {
350 //if (code == Code.MoveTo && ch == '%') { import core.stdc.stdio; printf("$-M: %g %g (cur: %g %g)\n", cast(double)args[0], cast(double)args[1], cx, cy); }
354 nvgTransformPoint(&vgx
, &vgy
, xf
[], cx
, cy
);
355 if (code
== Code
.MoveTo
) ctx
.moveTo(x0
+vgx
, y0
+vgy
); else ctx
.lineTo(x0
+vgx
, y0
+vgy
);
356 if (firstPoint || code
== Code
.MoveTo
) { firstx
= cx
; firsty
= cy
; firstPoint
= false; }
362 if (code
== Code
.HorizTo
) {
363 nvgTransformPoint(&vgx
, &vgy
, xf
[], cx
+args
[0], cy
);
366 nvgTransformPoint(&vgx
, &vgy
, xf
[], cx
, cy
+args
[0]);
369 ctx
.lineTo(x0
+vgx
, y0
+vgy
);
370 if (firstPoint
) { firstx
= cx
; firsty
= cy
; firstPoint
= false; }
373 nsvg__pathArcTo(&cx
, &cy
, args
[0..argc
], true);
376 immutable float x1
= cx
;
377 immutable float y1
= cy
;
380 immutable float x2
= x1
+args
[2];
381 immutable float y2
= y1
+args
[3];
382 immutable float cx1
= x1
+2.0f/3.0f*(cx
-x1
);
383 immutable float cy1
= y1
+2.0f/3.0f*(cy
-y1
);
384 immutable float cx2
= x2
+2.0f/3.0f*(cx
-x2
);
385 immutable float cy2
= y2
+2.0f/3.0f*(cy
-y2
);
396 if (firstPoint
) { firstx
= cx1
; firsty
= cy1
; firstPoint
= false; }
397 foreach (immutable pidx
; 0..3) nvgTransformPoint(args
[pidx
*2+0], args
[pidx
*2+1], xf
[]);
398 ctx
.bezierTo(x0
+args
[0], y0
+args
[1], x0
+args
[2], y0
+args
[3], x0
+args
[4], y0
+args
[5]);
400 case Code
.ShortQuadTo
:
401 immutable float x1
= cx
;
402 immutable float y1
= cy
;
403 immutable float x2
= cx
+args
[0];
404 immutable float y2
= cy
+args
[1];
409 // Convert to cubix bezier
410 immutable float cx1
= x1
+2.0f/3.0f*(cx
-x1
);
411 immutable float cy1
= y1
+2.0f/3.0f*(cy
-y1
);
412 immutable float cx2
= x2
+2.0f/3.0f*(cx
-x2
);
413 immutable float cy2
= y2
+2.0f/3.0f*(cy
-y2
);
424 if (firstPoint
) { firstx
= cx1
; firsty
= cy1
; firstPoint
= false; }
425 foreach (immutable pidx
; 0..3) nvgTransformPoint(args
[pidx
*2+0], args
[pidx
*2+1], xf
[]);
426 ctx
.bezierTo(x0
+args
[0], y0
+args
[1], x0
+args
[2], y0
+args
[3], x0
+args
[4], y0
+args
[5]);
436 //conwriteln("cx=", cx, "; cy=", cy);
438 import std
.math
: abs
;
439 if (abs(cx
-8) > 0.0001) { import core
.stdc
.stdio
; printf("char=%c; cx=%g\n", cast(int)ch
, cx
); }
440 if (abs(cy
) > 0.0001) { import core
.stdc
.stdio
; printf("char=%c; cy=%g\n", cast(int)ch
, cy
); }
445 nvgTransformPoint(cx
, cy
, xf
[]);
446 //nvg.moveTo(cx, cy);