summaryrefslogtreecommitdiff
path: root/src/nanovg.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/nanovg.cpp')
-rw-r--r--src/nanovg.cpp247
1 files changed, 247 insertions, 0 deletions
diff --git a/src/nanovg.cpp b/src/nanovg.cpp
new file mode 100644
index 0000000..06b2910
--- /dev/null
+++ b/src/nanovg.cpp
@@ -0,0 +1,247 @@
+
+enum nvg_point_flags
+{
+ NVG_PT_CORNER = 0x01,
+ NVG_PT_LEFT = 0x02,
+ NVG_PT_BEVEL = 0x04,
+ NVG_PR_INNERBEVEL = 0x08,
+};
+
+struct nvg_point
+{
+ real32 x;
+ real32 y;
+ real32 dx;
+ real32 dy;
+ real32 Length;
+ real32 dmx;
+ real32 dmy;
+ uint8 Flags;
+};
+
+static real32
+NVG_Normalize(real32 *x, float* y)
+{
+ real32 d = sqrtf((*x)*(*x) + (*y)*(*y));
+ if (d > 1e-6f) {
+ real32 id = 1.0f / d;
+ *x *= id;
+ *y *= id;
+ }
+ return d;
+}
+
+static real32 *
+NVG_Point(real32 *StrokeData, real32 x, real32 y, real32 u, real32 v)
+{
+ *(v4 *)StrokeData = V4(x, y, u, v);
+ return StrokeData + 4;
+}
+
+static void NVG_ChooseBevel(int bevel, nvg_point *p0, nvg_point *p1, float w,
+ float* x0, float* y0, float* x1, float* y1)
+{
+ if (bevel) {
+ *x0 = p1->x + p0->dy * w;
+ *y0 = p1->y - p0->dx * w;
+ *x1 = p1->x + p1->dy * w;
+ *y1 = p1->y - p1->dx * w;
+ } else {
+ *x0 = p1->x + p1->dmx * w;
+ *y0 = p1->y + p1->dmy * w;
+ *x1 = p1->x + p1->dmx * w;
+ *y1 = p1->y + p1->dmy * w;
+ }
+}
+
+static int NVG_Clampi(int a, int mn, int mx) { return a < mn ? mn : (a > mx ? mx : a); }
+
+static real32 * NVG_RoundJoin(nvg_point *Point, nvg_point *NextPoint, real32 *StrokeData,
+ float lw, float rw, float lu, float ru, int ncap)
+{
+ int i, n;
+ float dlx0 = Point->dy;
+ float dly0 = -Point->dx;
+ float dlx1 = NextPoint->dy;
+ float dly1 = -NextPoint->dx;
+
+ if (NextPoint->Flags & NVG_PT_LEFT) {
+ float lx0,ly0,lx1,ly1,a0,a1;
+ NVG_ChooseBevel(NextPoint->Flags & NVG_PR_INNERBEVEL, Point, NextPoint, lw, &lx0,&ly0, &lx1,&ly1);
+ a0 = atan2f(-dly0, -dlx0);
+ a1 = atan2f(-dly1, -dlx1);
+ if (a1 > a0) a1 -= PI*2;
+
+ StrokeData = NVG_Point(StrokeData, lx0, ly0, 0, 0);
+ StrokeData = NVG_Point(StrokeData, NextPoint->x - dlx0*rw, NextPoint->y - dly0*rw, 0, 0);
+
+ n = NVG_Clampi((int)ceilf(((a0 - a1) / PI) * ncap), 2, ncap);
+ for (i = 0; i < n; i++) {
+ float u = i/(float)(n-1);
+ float a = a0 + u*(a1-a0);
+ float rx = NextPoint->x + cosf(a) * rw;
+ float ry = NextPoint->y + sinf(a) * rw;
+ StrokeData = NVG_Point(StrokeData, NextPoint->x, NextPoint->y, 0, 0);
+ StrokeData = NVG_Point(StrokeData, rx, ry, 0, 0);
+ }
+
+ StrokeData = NVG_Point(StrokeData, lx1, ly1, 0, 0);
+ StrokeData = NVG_Point(StrokeData, NextPoint->x - dlx1*rw, NextPoint->y - dly1*rw, 0, 0);
+ }
+ return StrokeData;
+}
+
+static real32 * NVG_RoundCap(nvg_point * Point, real32 *StrokeData,
+ float dx, float dy, float w, int ncap,
+ float u0, float u1, int Mode)
+{
+ int i;
+ float px = Point->x;
+ float py = Point->y;
+ float dlx = dy;
+ float dly = -dx;
+ float Flip = (Mode == 0) ? 1 : -1;
+ if (Mode != 0) {
+ StrokeData = NVG_Point(StrokeData, px + dlx*w, py + dly*w, 0, 0);
+ StrokeData = NVG_Point(StrokeData, px - dlx*w, py - dly*w, 0, 0);
+ }
+ for (i = 0; i < ncap; i++) {
+ float a = i/(float)(ncap-1)*PI;
+ float ax = cosf(a) * w, ay = sinf(a) * w;
+ v2 OuterPoint = V2(px - dlx*ax - dx*ay*Flip, py - dly*ax - dy*ay*Flip);
+ v2 InnerPoint = V2(px, py);
+ if (Mode == 0) {
+ StrokeData = NVG_Point(StrokeData, OuterPoint.x, OuterPoint.y, 0, 0);
+ StrokeData = NVG_Point(StrokeData, InnerPoint.x, InnerPoint.y, 0, 0);
+ } else {
+ StrokeData = NVG_Point(StrokeData, InnerPoint.x, InnerPoint.y, 0, 0);
+ StrokeData = NVG_Point(StrokeData, OuterPoint.x, OuterPoint.y, 0, 0);
+ }
+ }
+ if (Mode == 0) {
+ StrokeData = NVG_Point(StrokeData, px + dlx*w, py + dly*w, 0, 0);
+ StrokeData = NVG_Point(StrokeData, px - dlx*w, py - dly*w, 0, 0);
+ }
+ return StrokeData;
+}
+
+// NOTE(fox): We only have to care about winding if we want to do HW accelerated
+// shape subtraction with the stencil buffer (I think).
+static uint32
+NVG_FlattenPath(void *Memory, block_bezier *Bezier, int PointCount, nvg_point *PointData, int *Width, int *Height)
+{
+ uint32 NumberOfVerts = 0;
+ nvg_point *PointPlayhead = PointData;
+ for (int i = 0; i < PointCount; i++) {
+ if (i == 0 || Bezier->Point[i].Type == interpolation_type_linear) {
+ *(v2 *)PointPlayhead = Bezier->Point[i].Pos[0];
+ if (i != 0 && i != (PointCount - 1)) {
+ PointPlayhead->Flags |= NVG_PT_CORNER;
+ }
+ PointPlayhead++;
+ NumberOfVerts++;
+ } else if (Bezier->Point[i].Type == interpolation_type_bezier) {
+ v2 Pos[4] = { Bezier->Point[i].Pos[0], Bezier->Point[i].Pos[1], Bezier->Point[i+1].Pos[2], Bezier->Point[i+1].Pos[0] };
+ Pos[1] = Pos[1] + Pos[0];
+ Pos[2] = Pos[2] + Pos[3];
+ NumberOfVerts += Bezier_CubicCalcPoints(Pos[3], Pos[2], Pos[1], Pos[0], PointPlayhead, sizeof(nvg_point));
+ // The point at the end is also returned, so we remove it.
+ NumberOfVerts--;
+ PointPlayhead--;
+ } else {
+ Assert(0);
+ }
+ }
+ nvg_point *Point = &PointData[NumberOfVerts - 1];
+ nvg_point *NextPoint = PointData;
+ v2 Min = V2(10000, 10000);
+ v2 Max = V2(-10000, -10000);
+ for (int i = 0; i < NumberOfVerts; i++) {
+ Point->dx = NextPoint->x - Point->x;
+ Point->dy = NextPoint->y - Point->y;
+ Point->Length = NVG_Normalize(&Point->dx, &Point->dy);
+ if (Point->x > Max.x)
+ Max.x = Point->x;
+ if (Point->x < Min.x)
+ Min.x = Point->x;
+ if (Point->y > Max.y)
+ Max.y = Point->y;
+ if (Point->y < Min.y)
+ Min.y = Point->y;
+ Point = NextPoint++;
+ }
+ *Width = Max.x - Min.x;
+ *Height = Max.y - Min.y;
+ return NumberOfVerts;
+}
+
+real32 MiterLimit = 2.4f;
+
+static uint32
+NVG_ExpandStroke(void *Memory, block_bezier *Bezier, int NumberOfVerts, nvg_point *PointData, real32 *StrokeData)
+{
+ real32 Width = 50 * 0.5;
+ nvg_point *Point = PointData;
+ nvg_point *NextPoint = &PointData[1];
+ int ncap = 12;
+ real32 *StartingStrokeData = StrokeData;
+
+ StrokeData = NVG_RoundCap(Point, StrokeData, Point->dx, Point->dy, Width, ncap, 0.5, 0.5, 0);
+
+ for (int i = 1; i < (NumberOfVerts - 1); i++) {
+
+ real32 dlx0, dly0, dlx1, dly1, dmr2, cross, limit;
+ dlx0 = Point->dy;
+ dly0 = -Point->dx;
+ dlx1 = NextPoint->dy;
+ dly1 = -NextPoint->dx;
+
+ // Calculate extrusions
+ NextPoint->dmx = (dlx0 + dlx1) * 0.5f;
+ NextPoint->dmy = (dly0 + dly1) * 0.5f;
+ dmr2 = NextPoint->dmx*NextPoint->dmx + NextPoint->dmy*NextPoint->dmy;
+ if (dmr2 > 0.000001f) {
+ float scale = 1.0f / dmr2;
+ if (scale > 600.0f) {
+ scale = 600.0f;
+ }
+ NextPoint->dmx *= scale;
+ NextPoint->dmy *= scale;
+ }
+
+ // Keep track of left turns.
+ cross = NextPoint->dx * Point->dy - Point->dx * NextPoint->dy;
+ if (cross > 0.0f) {
+ NextPoint->Flags |= NVG_PT_LEFT;
+ }
+
+ // Calculate if we should use bevel or miter for inner join.
+ // limit = nvg__maxf(1.01f, nvg__minf(p0->len, p1->len) * iw);
+ // if ((dmr2 * limit*limit) < 1.0f)
+ // p1->flags |= NVG_PR_INNERBEVEL;
+
+ // Check to see if the corner needs to be beveled.
+ if (NextPoint->Flags & NVG_PT_CORNER) {
+ // if ((dmr2 * MiterLimit*MiterLimit) < 1.0f) r // || lineJoin == NVG_BEVEL || lineJoin == NVG_ROUND) {
+ NextPoint->Flags |= NVG_PT_BEVEL;
+ // }
+ }
+
+ if ((NextPoint->Flags & (NVG_PT_BEVEL | NVG_PR_INNERBEVEL)) != 0) {
+ // if (lineJoin == NVG_ROUND) {
+ StrokeData = NVG_RoundJoin(Point, NextPoint, StrokeData, Width, Width, 0.5, 0.5, ncap);
+ // }
+ } else {
+ StrokeData = NVG_Point(StrokeData, NextPoint->x + (NextPoint->dmx * Width), NextPoint->y + (NextPoint->dmy * Width), 0, 0);
+ StrokeData = NVG_Point(StrokeData, NextPoint->x - (NextPoint->dmx * Width), NextPoint->y - (NextPoint->dmy * Width), 0, 0);
+ }
+
+ Point = NextPoint++;
+ }
+
+ StrokeData = NVG_RoundCap(NextPoint, StrokeData, Point->dx, Point->dy, Width, ncap, 0.5, 0.5, 1);
+
+ int GL_PointCount = (StrokeData - StartingStrokeData) / 4;
+
+ return GL_PointCount;
+}