mirror of https://github.com/adamdruppe/arsd.git
nanovega: it is now possible to "flatten" path outline
This commit is contained in:
parent
88ca1e2556
commit
a27deca9f1
139
nanovega.d
139
nanovega.d
|
@ -7644,7 +7644,7 @@ private nothrow @trusted @nogc:
|
||||||
if (used+cast(uint)b.length > size) {
|
if (used+cast(uint)b.length > size) {
|
||||||
import core.stdc.stdlib : realloc;
|
import core.stdc.stdlib : realloc;
|
||||||
uint newsz = size;
|
uint newsz = size;
|
||||||
while (newsz < used+cast(uint)b.length) newsz = (newsz == 0 ? 2048 : newsz < 32768 ? newsz*2 : newsz+8192);
|
while (newsz < used+cast(uint)b.length) newsz = (newsz == 0 ? 1024 : newsz < 32768 ? newsz*2 : newsz+8192);
|
||||||
assert(used+cast(uint)b.length <= newsz);
|
assert(used+cast(uint)b.length <= newsz);
|
||||||
data = cast(ubyte*)realloc(data, newsz);
|
data = cast(ubyte*)realloc(data, newsz);
|
||||||
if (data is null) assert(0, "NanoVega: out of memory");
|
if (data is null) assert(0, "NanoVega: out of memory");
|
||||||
|
@ -7709,11 +7709,21 @@ public:
|
||||||
QuadTo, ///
|
QuadTo, ///
|
||||||
BezierTo, ///
|
BezierTo, ///
|
||||||
End, /// no more commands (this command is not `valid`!)
|
End, /// no more commands (this command is not `valid`!)
|
||||||
|
|
||||||
}
|
}
|
||||||
Kind code; ///
|
Kind code; ///
|
||||||
const(float)[] args; ///
|
const(float)[] args; ///
|
||||||
@property bool valid () const pure nothrow @safe @nogc { pragma(inline, true); return (code >= Kind.min && code < Kind.End && args.length >= 2); } ///
|
@property bool valid () const pure nothrow @safe @nogc { pragma(inline, true); return (code >= Kind.min && code < Kind.End && args.length >= 2); } ///
|
||||||
|
|
||||||
|
static uint arglen (Kind code) pure nothrow @safe @nogc {
|
||||||
|
pragma(inline, true);
|
||||||
|
return
|
||||||
|
code == Kind.MoveTo || code == Kind.LineTo ? 2 :
|
||||||
|
code == Kind.QuadTo ? 4 :
|
||||||
|
code == Kind.BezierTo ? 6 :
|
||||||
|
0;
|
||||||
|
}
|
||||||
|
|
||||||
/// perform NanoVega command with stored data.
|
/// perform NanoVega command with stored data.
|
||||||
void perform (NVGContext ctx) const nothrow @trusted @nogc {
|
void perform (NVGContext ctx) const nothrow @trusted @nogc {
|
||||||
if (ctx is null) return;
|
if (ctx is null) return;
|
||||||
|
@ -7742,6 +7752,27 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
/// Create new path with quadratic bezier (first command is MoveTo, second command is QuadTo).
|
||||||
|
static NVGPathOutline createNewQuad (in float x0, in float y0, in float cx, in float cy, in float x, in float y) {
|
||||||
|
auto res = createNew();
|
||||||
|
res.ds.putCommand(Command.Kind.MoveTo);
|
||||||
|
res.ds.putArgs(x0, y0);
|
||||||
|
res.ds.putCommand(Command.Kind.QuadTo);
|
||||||
|
res.ds.putArgs(cx, cy, x, y);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create new path with cubic bezier (first command is MoveTo, second command is BezierTo).
|
||||||
|
static NVGPathOutline createNewBezier (in float x1, in float y1, in float x2, in float y2, in float x3, in float y3, in float x4, in float y4) {
|
||||||
|
auto res = createNew();
|
||||||
|
res.ds.putCommand(Command.Kind.MoveTo);
|
||||||
|
res.ds.putArgs(x1, y1);
|
||||||
|
res.ds.putCommand(Command.Kind.BezierTo);
|
||||||
|
res.ds.putArgs(x2, y2, x3, y3, x4, y4);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
this (this) { pragma(inline, true); incRef(cast(DataStore*)dsaddr); }
|
this (this) { pragma(inline, true); incRef(cast(DataStore*)dsaddr); }
|
||||||
~this () { pragma(inline, true); decRef(cast(DataStore*)dsaddr); }
|
~this () { pragma(inline, true); decRef(cast(DataStore*)dsaddr); }
|
||||||
|
@ -7765,8 +7796,112 @@ public:
|
||||||
/// Returns umber of commands in outline.
|
/// Returns umber of commands in outline.
|
||||||
@property int length () const pure { pragma(inline, true); return (dsaddr ? ds.ccount : 0); }
|
@property int length () const pure { pragma(inline, true); return (dsaddr ? ds.ccount : 0); }
|
||||||
|
|
||||||
|
/// Returns "flattened" path. Flattened path consists of only two commands kinds: MoveTo and LineTo.
|
||||||
|
NVGPathOutline flatten () const { pragma(inline, true); return flattenInternal(null); }
|
||||||
|
|
||||||
|
/// Returns "flattened" path, transformed by the given matrix. Flattened path consists of only two commands kinds: MoveTo and LineTo.
|
||||||
|
NVGPathOutline flatten() (in auto ref NVGMatrix mt) const { pragma(inline, true); return flattenInternal(&mt); }
|
||||||
|
|
||||||
|
// Returns "flattened" path, transformed by the given matrix. Flattened path consists of only two commands kinds: MoveTo and LineTo.
|
||||||
|
private NVGPathOutline flattenInternal (scope NVGMatrix* tfm) const {
|
||||||
|
NVGPathOutline res;
|
||||||
|
if (dsaddr == 0) { res = this; return res; } // nothing to do
|
||||||
|
// check if we need to flatten the path
|
||||||
|
bool dowork = false;
|
||||||
|
foreach (const ref cs; commands) {
|
||||||
|
if (cs.code != Command.Kind.MoveTo && cs.code != Command.Kind.LineTo) {
|
||||||
|
dowork = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!dowork) { res = this; return res; } // nothing to do
|
||||||
|
// has some curves, convert path
|
||||||
|
res = createNew();
|
||||||
|
|
||||||
|
void addPoint (float x, float y, Command.Kind cmd=Command.Kind.LineTo) {
|
||||||
|
res.ds.putCommand(cmd);
|
||||||
|
if (tfm !is null) tfm.point(x, y);
|
||||||
|
res.ds.putArgs(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
// sorry for this pasta
|
||||||
|
void flattenBezier (in float x1, in float y1, in float x2, in float y2, in float x3, in float y3, in float x4, in float y4, in int level) nothrow @trusted @nogc {
|
||||||
|
if (level > 10) return;
|
||||||
|
|
||||||
|
immutable float x12 = (x1+x2)*0.5f;
|
||||||
|
immutable float y12 = (y1+y2)*0.5f;
|
||||||
|
immutable float x23 = (x2+x3)*0.5f;
|
||||||
|
immutable float y23 = (y2+y3)*0.5f;
|
||||||
|
immutable float x34 = (x3+x4)*0.5f;
|
||||||
|
immutable float y34 = (y3+y4)*0.5f;
|
||||||
|
immutable float x123 = (x12+x23)*0.5f;
|
||||||
|
immutable float y123 = (y12+y23)*0.5f;
|
||||||
|
|
||||||
|
immutable float dx = x4-x1;
|
||||||
|
immutable float dy = y4-y1;
|
||||||
|
immutable float d2 = nvg__absf(((x2-x4)*dy-(y2-y4)*dx));
|
||||||
|
immutable float d3 = nvg__absf(((x3-x4)*dy-(y3-y4)*dx));
|
||||||
|
|
||||||
|
if ((d2+d3)*(d2+d3) < /*ctx.tessTol*/0.25f*(dx*dx+dy*dy)) {
|
||||||
|
addPoint(x4, y4);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
immutable float x234 = (x23+x34)*0.5f;
|
||||||
|
immutable float y234 = (y23+y34)*0.5f;
|
||||||
|
immutable float x1234 = (x123+x234)*0.5f;
|
||||||
|
immutable float y1234 = (y123+y234)*0.5f;
|
||||||
|
|
||||||
|
|
||||||
|
// "taxicab" / "manhattan" check for flat curves
|
||||||
|
if (nvg__absf(x1+x3-x2-x2)+nvg__absf(y1+y3-y2-y2)+nvg__absf(x2+x4-x3-x3)+nvg__absf(y2+y4-y3-y3) < /*ctx.tessTol*/0.25f/4) {
|
||||||
|
addPoint(x1234, y1234);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
flattenBezier(x1, y1, x12, y12, x123, y123, x1234, y1234, level+1);
|
||||||
|
flattenBezier(x1234, y1234, x234, y234, x34, y34, x4, y4, level+1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void flattenQuad (in float x0, in float y0, in float cx, in float cy, in float x, in float y) {
|
||||||
|
flattenBezier(
|
||||||
|
x0, y0,
|
||||||
|
x0+2.0f/3.0f*(cx-x0), y0+2.0f/3.0f*(cy-y0),
|
||||||
|
x+2.0f/3.0f*(cx-x), y+2.0f/3.0f*(cy-y),
|
||||||
|
x, y,
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
float cx = 0, cy = 0;
|
||||||
|
foreach (const ref cs; commands) {
|
||||||
|
switch (cs.code) {
|
||||||
|
case Command.Kind.LineTo:
|
||||||
|
case Command.Kind.MoveTo:
|
||||||
|
addPoint(cs.args[0], cs.args[1], cs.code);
|
||||||
|
cx = cs.args[0];
|
||||||
|
cy = cs.args[1];
|
||||||
|
break;
|
||||||
|
case Command.Kind.QuadTo:
|
||||||
|
flattenQuad(cx, cy, cs.args[0], cs.args[1], cs.args[2], cs.args[3]);
|
||||||
|
cx = cs.args[2];
|
||||||
|
cy = cs.args[3];
|
||||||
|
break;
|
||||||
|
case Command.Kind.BezierTo:
|
||||||
|
flattenBezier(cx, cy, cs.args[0], cs.args[1], cs.args[2], cs.args[3], cs.args[4], cs.args[5], 0);
|
||||||
|
cx = cs.args[4];
|
||||||
|
cy = cs.args[5];
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns forward range with all glyph commands.
|
/// Returns forward range with all glyph commands.
|
||||||
auto commands () nothrow @trusted @nogc {
|
auto commands () const nothrow @trusted @nogc {
|
||||||
static struct Range {
|
static struct Range {
|
||||||
private nothrow @trusted @nogc:
|
private nothrow @trusted @nogc:
|
||||||
usize dsaddr;
|
usize dsaddr;
|
||||||
|
|
Loading…
Reference in New Issue