Add initial wrapping code and example
This commit is contained in:
parent
a59af87971
commit
93057d3709
13 changed files with 1041 additions and 0 deletions
255
examples/demo.nim
Normal file
255
examples/demo.nim
Normal file
|
|
@ -0,0 +1,255 @@
|
||||||
|
import os
|
||||||
|
import nanovg
|
||||||
|
import glfw, opengl
|
||||||
|
import perf
|
||||||
|
|
||||||
|
type
|
||||||
|
WindowConfig = object
|
||||||
|
cornerRadiusTopLeft: float
|
||||||
|
cornerRadiusTopRight: float
|
||||||
|
cornerRadiusBottomLeft: float
|
||||||
|
cornerRadiusBottomRight: float
|
||||||
|
|
||||||
|
# const ICON_TRASH: cint = 0x0000f1f8
|
||||||
|
const ICON_TRASH = ""
|
||||||
|
# const ICON_TRASH = "\uf1f8"
|
||||||
|
#const ICON_TRASH: cint = 0xE729
|
||||||
|
|
||||||
|
proc currentSourceDir(): string {.inline.} =
|
||||||
|
return currentSourcePath.parentDir()
|
||||||
|
|
||||||
|
# Config
|
||||||
|
|
||||||
|
proc drawLabel(text: string, x, y, w, h: float) =
|
||||||
|
nanovg.setFontSize(50.0)
|
||||||
|
nanovg.setFont("sans")
|
||||||
|
nanovg.setFillColor(1.0, 1.0, 1.0, 1.0)
|
||||||
|
|
||||||
|
nanovg.textAlign("left middle")
|
||||||
|
nanovg.text(x, y+h*0.5, text)
|
||||||
|
|
||||||
|
proc drawWindow(title: string, x, y, w, h: float) =
|
||||||
|
let cornerRadius = 3.0
|
||||||
|
withState:
|
||||||
|
nanovg.beginPath()
|
||||||
|
nanovg.pathRoundedRect(x, y, w, h, cornerRadius)
|
||||||
|
#nanovg.setFillColor(RGBA(255,255,255,192))
|
||||||
|
nanovg.setFillColor(RGBA(28,30,34,192))
|
||||||
|
nanovg.pathFill()
|
||||||
|
|
||||||
|
nanovg.setFontSize(30.0)
|
||||||
|
nanovg.setFont("sans")
|
||||||
|
nanovg.textAlign("center middle")
|
||||||
|
|
||||||
|
nanovg.setFontBlur(2)
|
||||||
|
nanovg.setFillColor(RGBA(0,0,0,128))
|
||||||
|
nanovg.text(x+w/2, y+16+1, title)
|
||||||
|
|
||||||
|
nanovg.setFontBlur(0)
|
||||||
|
nanovg.setFillColor(RGBA(220,220,220,160))
|
||||||
|
nanovg.text(x+w/2,y+16, title)
|
||||||
|
|
||||||
|
proc isBlack(color: Color): bool =
|
||||||
|
result = true
|
||||||
|
|
||||||
|
for n, v in color.fieldPairs:
|
||||||
|
if v != 0:
|
||||||
|
return false
|
||||||
|
|
||||||
|
proc cpToUtf8*(code: int): string =
|
||||||
|
var n = 0
|
||||||
|
var cp = code
|
||||||
|
|
||||||
|
if cp < 0x80: n = 1
|
||||||
|
elif cp < 0x800: n = 2
|
||||||
|
elif cp < 0x10000: n = 3
|
||||||
|
elif cp < 0x200000: n = 4
|
||||||
|
elif cp < 0x4000000: n = 5
|
||||||
|
elif cp <= 0x7fffffff: n = 6
|
||||||
|
|
||||||
|
var res = newStringOfCap(n)
|
||||||
|
res.setLen(n)
|
||||||
|
|
||||||
|
if n >= 6:
|
||||||
|
res[5] = char(0x80 or (cp and 0x3f))
|
||||||
|
cp = cp shr 6
|
||||||
|
cp = cp or 0x4000000
|
||||||
|
if n >= 5:
|
||||||
|
res[4] = char(0x80 or (cp and 0x3f))
|
||||||
|
cp = cp shr 6
|
||||||
|
cp = cp or 0x200000
|
||||||
|
if n >= 4:
|
||||||
|
res[3] = char(0x80 or (cp and 0x3f))
|
||||||
|
cp = cp shr 6
|
||||||
|
cp = cp or 0x10000
|
||||||
|
if n >= 3:
|
||||||
|
res[2] = char(0x80 or (cp and 0x3f))
|
||||||
|
cp = cp shr 6
|
||||||
|
cp = cp or 0x800
|
||||||
|
if n >= 2:
|
||||||
|
res[1] = char(0x80 or (cp and 0x3f))
|
||||||
|
cp = cp shr 6
|
||||||
|
cp = cp or 0xc0
|
||||||
|
if n >= 1:
|
||||||
|
res[0] = char(cp)
|
||||||
|
|
||||||
|
return res
|
||||||
|
|
||||||
|
proc drawButton*(text: string, x, y, width, height: float) =
|
||||||
|
let bg = linearGradient(
|
||||||
|
x, y, x, y+height,
|
||||||
|
RGBA(255, 255, 255, 16),
|
||||||
|
RGBA(100, 100, 100, 16)
|
||||||
|
)
|
||||||
|
|
||||||
|
let color = RGBA(29, 100, 210, 255)
|
||||||
|
let shadowColor = RGBA(0, 0, 0, 90)
|
||||||
|
let highlightColor = RGBA(255, 255, 255, 100)
|
||||||
|
let radius = 3.0
|
||||||
|
let lightWidth = 0.5
|
||||||
|
let shadowOffset = 3.0
|
||||||
|
let drawShadow = true
|
||||||
|
|
||||||
|
if drawShadow:
|
||||||
|
nanovg.beginPath()
|
||||||
|
nanovg.pathRoundedRect(x+1+shadowOffset, y+1+shadowOffset, width-2, height-2, radius)
|
||||||
|
nanovg.setFillColor(shadowColor)
|
||||||
|
nanovg.pathFill()
|
||||||
|
|
||||||
|
nanovg.beginPath()
|
||||||
|
nanovg.pathRoundedRect(x+1, y+1, width-2, height-2, radius)
|
||||||
|
nanovg.setFillColor(color)
|
||||||
|
nanovg.pathFill()
|
||||||
|
|
||||||
|
nanovg.setFillPaint(bg)
|
||||||
|
nanovg.pathFill()
|
||||||
|
|
||||||
|
nanovg.beginPath()
|
||||||
|
nanovg.pathMoveTo(x+radius, y+lightWidth+1)
|
||||||
|
nanovg.setStrokeWidth(lightWidth)
|
||||||
|
nanovg.setStrokeColor(highlightColor)
|
||||||
|
nanovg.pathLineTo(x+width-(radius), y+lightWidth+1)
|
||||||
|
nanovg.pathStroke()
|
||||||
|
|
||||||
|
|
||||||
|
proc drawButton*(preicon: string, text: string, x, y, w, h: float, color: Color) =
|
||||||
|
|
||||||
|
var
|
||||||
|
bg: Paint
|
||||||
|
cornerRadius = 4.0
|
||||||
|
tb: TextBounds
|
||||||
|
ib: TextBounds
|
||||||
|
tw = 0.0
|
||||||
|
iw = 0.0
|
||||||
|
|
||||||
|
bg = nanovg.linearGradient(
|
||||||
|
x,y,x,y+h,
|
||||||
|
RGBA(255,255,255, if isBlack(color): 16 else:32),
|
||||||
|
RGBA(0,0,0, if isBlack(color): 16 else:32)
|
||||||
|
)
|
||||||
|
|
||||||
|
nanovg.beginPath()
|
||||||
|
nanovg.pathRoundedRect(x+1, y+1, w-2, h-2, cornerRadius-1)
|
||||||
|
|
||||||
|
if not isBlack(color):
|
||||||
|
nanovg.setFillColor(color)
|
||||||
|
nanovg.pathFill()
|
||||||
|
|
||||||
|
nanovg.setFillPaint(bg)
|
||||||
|
nanovg.pathFill()
|
||||||
|
|
||||||
|
nanovg.beginPath()
|
||||||
|
nanovg.pathRoundedRect(x+0.5, y+0.5, w-1, h-1, cornerRadius-0.5)
|
||||||
|
nanovg.setStrokeColor(RGBA(0,0,0,48))
|
||||||
|
nanovg.pathStroke()
|
||||||
|
|
||||||
|
nanovg.setFontSize(20.0)
|
||||||
|
nanovg.setFont("sans-bold")
|
||||||
|
tb = nanovg.textBounds(0.0, 0.0, text)
|
||||||
|
tw = tb.horizontalAdvance
|
||||||
|
|
||||||
|
nanovg.setFontSize(h*0.5)
|
||||||
|
nanovg.setFont("fa")
|
||||||
|
ib = nanovg.textBounds(0,0, preicon)
|
||||||
|
iw = ib.horizontalAdvance
|
||||||
|
iw += h*0.15
|
||||||
|
|
||||||
|
nanovg.setFontSize(h*0.5)
|
||||||
|
nanovg.setFont("fa")
|
||||||
|
nanovg.setFillColor(RGBA(255,255,255,96))
|
||||||
|
nanovg.textAlign("left middle")
|
||||||
|
nanovg.text(x+w*0.5-tw*0.5-iw*0.75, y+h*0.5, preicon)
|
||||||
|
|
||||||
|
nanovg.setFontSize(20.0)
|
||||||
|
nanovg.setFont("sans-bold")
|
||||||
|
nanovg.textAlign("left middle")
|
||||||
|
nanovg.setFillColor(RGBA(0,0,0,160))
|
||||||
|
nanovg.text(x+w*0.5-tw*0.5+iw*0.25, y+h*0.5-1, text)
|
||||||
|
nanovg.setFillColor(RGBA(255,255,255,160))
|
||||||
|
nanovg.text(x+w*0.5-tw*0.5+iw*0.25, y+h*0.5, text)
|
||||||
|
|
||||||
|
proc main =
|
||||||
|
glfw.initialize()
|
||||||
|
|
||||||
|
var c = DefaultOpenglWindowConfig
|
||||||
|
c.version = glv32
|
||||||
|
c.title = "Minimal Nim-GLFW example"
|
||||||
|
c.forwardCompat = true
|
||||||
|
c.profile = opCoreProfile
|
||||||
|
c.debugContext = false
|
||||||
|
|
||||||
|
let
|
||||||
|
fps = newGraph(RenderStyle.FPS, "Frame Time")
|
||||||
|
cpuGraph = newGraph(RenderStyle.MS, "CPU Time")
|
||||||
|
|
||||||
|
|
||||||
|
var w = newWindow(c)
|
||||||
|
echo currentSourcePath
|
||||||
|
nanovg.init()
|
||||||
|
nanovg.loadFont(currentSourceDir()/"resources/Roboto-Regular.ttf", "sans")
|
||||||
|
nanovg.loadFont(currentSourceDir()/"resources/Roboto-Bold.ttf", "sans-bold")
|
||||||
|
nanovg.loadFont(currentSourceDir()/"resources/fa.ttf", "fa")
|
||||||
|
nanovg.loadFont(currentSourceDir()/"resources/entypo.ttf", "icons")
|
||||||
|
swapInterval(0)
|
||||||
|
setTime(0)
|
||||||
|
|
||||||
|
var prevt = getTime()
|
||||||
|
|
||||||
|
while not w.shouldClose():
|
||||||
|
let t = getTime()
|
||||||
|
let dt = t - prevt
|
||||||
|
prevt = t
|
||||||
|
|
||||||
|
#let pos = w.cursorPos()
|
||||||
|
let fbSize = w.frameBufferSize()
|
||||||
|
|
||||||
|
glViewPort(0, 0, fbSize.w, fbSize.h)
|
||||||
|
glCullFace(GL_BACK)
|
||||||
|
glFrontFace(GL_CCW)
|
||||||
|
glClearColor(0.3, 0.3, 0.32, 1.0)
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT or GL_STENCIL_BUFFER_BIT)
|
||||||
|
|
||||||
|
withFrame(w):
|
||||||
|
let x = glyphPositions("Some仮名thing is up", 0, 0)
|
||||||
|
drawWindow("Title", 50, 50, 300, 400)
|
||||||
|
drawLabel("Hello!", 10, 10, 280, 20)
|
||||||
|
drawButton(ICON_TRASH, "Delete", 100, 200, 160, 28, RGBA(128, 16, 8, 255))
|
||||||
|
drawButton(ICON_TRASH, "Deleter", 100, 250, 160, 28, RGBA(12, 130, 80, 255))
|
||||||
|
drawButton("Testing", 100, 300, 160, 28)
|
||||||
|
|
||||||
|
fps.render(5, 5)
|
||||||
|
cpuGraph.render(210, 5)
|
||||||
|
|
||||||
|
w.swapBuffers()
|
||||||
|
pollEvents()
|
||||||
|
|
||||||
|
let cpuTime = glfw.getTime() - t
|
||||||
|
|
||||||
|
fps.update(dt)
|
||||||
|
cpuGraph.update(cpuTime)
|
||||||
|
waitEvents()
|
||||||
|
|
||||||
|
w.destroy()
|
||||||
|
glfw.terminate()
|
||||||
|
|
||||||
|
main()
|
||||||
1
examples/nim.cfg
Normal file
1
examples/nim.cfg
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
--path:"../src/"
|
||||||
123
examples/perf.nim
Normal file
123
examples/perf.nim
Normal file
|
|
@ -0,0 +1,123 @@
|
||||||
|
import nanovg
|
||||||
|
import strformat
|
||||||
|
|
||||||
|
const GRAPH_HISTORY_COUNT = 100
|
||||||
|
|
||||||
|
type
|
||||||
|
RenderStyle* {.pure.} = enum
|
||||||
|
FPS,
|
||||||
|
MS,
|
||||||
|
PERCENT
|
||||||
|
|
||||||
|
PerfGraph* = ref object
|
||||||
|
style: RenderStyle
|
||||||
|
name: string
|
||||||
|
values: array[GRAPH_HISTORY_COUNT, float]
|
||||||
|
head: int
|
||||||
|
|
||||||
|
|
||||||
|
proc newGraph*(style: RenderStyle, name: string): PerfGraph =
|
||||||
|
new(result)
|
||||||
|
result.style = style
|
||||||
|
result.name = name
|
||||||
|
|
||||||
|
proc update*(graph: PerfGraph, frameTime: float) =
|
||||||
|
graph.head = (graph.head + 1) mod GRAPH_HISTORY_COUNT
|
||||||
|
graph.values[graph.head] = frameTime
|
||||||
|
|
||||||
|
proc getGraphAverage(graph: PerfGraph): float =
|
||||||
|
var avg: float = 0.0
|
||||||
|
for i in 0 ..< GRAPH_HISTORY_COUNT:
|
||||||
|
avg += graph.values[i]
|
||||||
|
|
||||||
|
return avg / GRAPH_HISTORY_COUNT.float
|
||||||
|
|
||||||
|
proc render*(graph: PerfGraph, x, y: float) =
|
||||||
|
let
|
||||||
|
avg = getGraphAverage(graph)
|
||||||
|
width: float = 200
|
||||||
|
height: float = 35
|
||||||
|
|
||||||
|
nanovg.beginPath()
|
||||||
|
nanovg.pathRect(x, y, width, height)
|
||||||
|
nanovg.setFillColor(0, 0, 0, 128)
|
||||||
|
nanovg.pathFill()
|
||||||
|
|
||||||
|
nanovg.beginPath()
|
||||||
|
nanovg.pathMoveTo(x, y+height)
|
||||||
|
|
||||||
|
case graph.style:
|
||||||
|
of RenderStyle.FPS:
|
||||||
|
for i in 0 ..< GRAPH_HISTORY_COUNT:
|
||||||
|
var v: float = 1.0 / (0.00001 + graph.values[(graph.head+i) mod GRAPH_HISTORY_COUNT])
|
||||||
|
var vx, vy: float
|
||||||
|
|
||||||
|
if v > 80:
|
||||||
|
v = 80
|
||||||
|
|
||||||
|
vx = x + (i/(GRAPH_HISTORY_COUNT-1)) * width
|
||||||
|
vy = y + height - ((v / 80) * height)
|
||||||
|
|
||||||
|
nanovg.pathLineTo(vx, vy)
|
||||||
|
|
||||||
|
of RenderStyle.PERCENT:
|
||||||
|
for i in 0 ..< GRAPH_HISTORY_COUNT:
|
||||||
|
var v: float = graph.values[(graph.head+i) mod GRAPH_HISTORY_COUNT]
|
||||||
|
var vx, vy: float
|
||||||
|
|
||||||
|
if v > 100:
|
||||||
|
v = 100
|
||||||
|
|
||||||
|
vx = x + (i/(GRAPH_HISTORY_COUNT-1)) * width
|
||||||
|
vy = y + height - ((v / 100) * height)
|
||||||
|
|
||||||
|
nanovg.pathLineTo(vx, vy)
|
||||||
|
|
||||||
|
of RenderStyle.MS:
|
||||||
|
for i in 0 ..< GRAPH_HISTORY_COUNT:
|
||||||
|
var v: float = graph.values[(graph.head+i) mod GRAPH_HISTORY_COUNT] * 1000
|
||||||
|
var vx, vy: float
|
||||||
|
|
||||||
|
if v > 20:
|
||||||
|
v = 20
|
||||||
|
|
||||||
|
vx = x + (i/(GRAPH_HISTORY_COUNT-1)) * width
|
||||||
|
vy = y + height - ((v / 20) * height)
|
||||||
|
|
||||||
|
nanovg.pathLineTo(vx, vy)
|
||||||
|
|
||||||
|
nanovg.pathLineTo(x+width, y+height)
|
||||||
|
nanovg.setFillColor(255, 192, 0, 128)
|
||||||
|
nanovg.pathFill()
|
||||||
|
|
||||||
|
nanovg.setFont("sans")
|
||||||
|
|
||||||
|
if graph.name.len() > 0:
|
||||||
|
nanovg.setFontSize(14)
|
||||||
|
nanovg.textAlign("top left")
|
||||||
|
nanovg.setFillColor(240, 240, 240, 192)
|
||||||
|
nanovg.text(x+3, y+1, graph.name)
|
||||||
|
|
||||||
|
nanovg.setFontSize(18)
|
||||||
|
nanovg.textAlign("top right")
|
||||||
|
nanovg.setFillColor(240, 240, 240, 255)
|
||||||
|
|
||||||
|
case graph.style:
|
||||||
|
of RenderStyle.FPS:
|
||||||
|
var str = fmt"{1.0/avg:.2f} FPS"
|
||||||
|
|
||||||
|
nanovg.text(x+width-3, y+1, str)
|
||||||
|
nanovg.setFontSize(15)
|
||||||
|
nanovg.textAlign("bottom right")
|
||||||
|
nanovg.setFillColor(240, 240, 240, 160)
|
||||||
|
|
||||||
|
str = fmt"{avg*1000:.2f} ms"
|
||||||
|
nanovg.text(x+width-3, y+height-1, str)
|
||||||
|
|
||||||
|
of RenderStyle.PERCENT:
|
||||||
|
let str = fmt"{avg:.1f} %"
|
||||||
|
nanovg.text(x+width-3, y+1, str)
|
||||||
|
|
||||||
|
of RenderStyle.MS:
|
||||||
|
let str = fmt"{avg*1000:.2f} ms"
|
||||||
|
nanovg.text(x+width-3, y+1, str)
|
||||||
BIN
examples/resources/NotoEmoji-Regular.ttf
Executable file
BIN
examples/resources/NotoEmoji-Regular.ttf
Executable file
Binary file not shown.
BIN
examples/resources/Roboto-Bold.ttf
Executable file
BIN
examples/resources/Roboto-Bold.ttf
Executable file
Binary file not shown.
BIN
examples/resources/Roboto-Light.ttf
Executable file
BIN
examples/resources/Roboto-Light.ttf
Executable file
Binary file not shown.
BIN
examples/resources/Roboto-Regular.ttf
Executable file
BIN
examples/resources/Roboto-Regular.ttf
Executable file
Binary file not shown.
BIN
examples/resources/entypo.ttf
Executable file
BIN
examples/resources/entypo.ttf
Executable file
Binary file not shown.
BIN
examples/resources/fa.ttf
Executable file
BIN
examples/resources/fa.ttf
Executable file
Binary file not shown.
17
nanovg.nimble
Normal file
17
nanovg.nimble
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
# Package
|
||||||
|
|
||||||
|
version = "0.1.0"
|
||||||
|
author = "Joey Yakimowich-Payne"
|
||||||
|
description = "A wrapper around nanovg"
|
||||||
|
license = "MIT"
|
||||||
|
srcDir = "src"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Dependencies
|
||||||
|
|
||||||
|
requires "nim >= 1.0.0",
|
||||||
|
"nimterop >= 0.2.1",
|
||||||
|
"regex >= 0.12.0",
|
||||||
|
"opengl >= 1.2.2",
|
||||||
|
"https://github.com/jyapayne/nim-glfw#d4f30db"
|
||||||
2
nim.cfg
Normal file
2
nim.cfg
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
-d:glfwStaticLib
|
||||||
|
-d:useGlew
|
||||||
546
src/nanovg.nim
Normal file
546
src/nanovg.nim
Normal file
|
|
@ -0,0 +1,546 @@
|
||||||
|
import macros
|
||||||
|
import sequtils
|
||||||
|
import typetraits
|
||||||
|
import strutils
|
||||||
|
import strformat
|
||||||
|
import nanovg/wrapper
|
||||||
|
|
||||||
|
converter intToCuchar*(source: uint8 | int): cuchar =
|
||||||
|
return source.cuchar
|
||||||
|
|
||||||
|
export HSL
|
||||||
|
export HSLA
|
||||||
|
export RGBA
|
||||||
|
export RGBAf
|
||||||
|
export RGB
|
||||||
|
export RGBf
|
||||||
|
export LerpRGBA
|
||||||
|
export TransRGBA
|
||||||
|
export TransRGBAf
|
||||||
|
|
||||||
|
#{.experimental: "dynamicBindSym".}
|
||||||
|
|
||||||
|
type
|
||||||
|
FontLoadError* = object of Exception
|
||||||
|
ImageLoadError* = object of Exception
|
||||||
|
ContextInitError* = object of Exception
|
||||||
|
Context = ptr NVGContext
|
||||||
|
|
||||||
|
Alignment* {.size: sizeof(cint), pure.} = enum
|
||||||
|
NONE = 0
|
||||||
|
# Horizontal
|
||||||
|
LEFT = NVG_ALIGN_LEFT
|
||||||
|
CENTER = NVG_ALIGN_CENTER
|
||||||
|
RIGHT = NVG_ALIGN_RIGHT
|
||||||
|
|
||||||
|
# Vertical
|
||||||
|
TOP = NVG_ALIGN_TOP
|
||||||
|
MIDDLE = NVG_ALIGN_MIDDLE
|
||||||
|
BOTTOM = NVG_ALIGN_BOTTOM
|
||||||
|
BASELINE = NVG_ALIGN_BASELINE # Default
|
||||||
|
|
||||||
|
LineCap* {.size: sizeof(cint), pure.} = enum
|
||||||
|
BUTT = NVG_BUTT
|
||||||
|
ROUND = NVG_ROUND
|
||||||
|
SQUARE = NVG_SQUARE
|
||||||
|
BEVEL = NVG_BEVEL
|
||||||
|
MITER = NVG_MITER
|
||||||
|
|
||||||
|
LineJoin* {.size: sizeof(cint), pure.} = enum
|
||||||
|
ROUND = NVG_ROUND
|
||||||
|
BEVEL = NVG_BEVEL
|
||||||
|
MITER = NVG_MITER
|
||||||
|
|
||||||
|
CompositeOperation* {.size: sizeof(cint), pure.} = enum
|
||||||
|
SOURCE_OVER, SOURCE_IN, SOURCE_OUT, ATOP, DESTINATION_OVER,
|
||||||
|
DESTINATION_IN, DESTINATION_OUT, DESTINATION_ATOP, LIGHTER,
|
||||||
|
COPY, XOR
|
||||||
|
|
||||||
|
BlendFactor* {.size: sizeof(cint), pure.} = enum
|
||||||
|
ZERO = NVG_ZERO, ONE = NVG_ONE, SRC_COLOR = NVG_SRC_COLOR,
|
||||||
|
ONE_MINUS_SRC_COLOR = NVG_ONE_MINUS_SRC_COLOR, DST_COLOR = NVG_DST_COLOR,
|
||||||
|
ONE_MINUS_DST_COLOR = NVG_ONE_MINUS_DST_COLOR, SRC_ALPHA = NVG_SRC_ALPHA,
|
||||||
|
ONE_MINUS_SRC_ALPHA = NVG_ONE_MINUS_SRC_ALPHA, DST_ALPHA = NVG_DST_ALPHA,
|
||||||
|
ONE_MINUS_DST_ALPHA = NVG_ONE_MINUS_DST_ALPHA, SRC_ALPHA_SATURATE = NVG_SRC_ALPHA_SATURATE
|
||||||
|
|
||||||
|
Bounds* = tuple[xmin, ymin, xmax, ymax: float]
|
||||||
|
|
||||||
|
TextBounds* = tuple[horizontalAdvance: float, bounds: Bounds]
|
||||||
|
|
||||||
|
Size* = tuple[width, height: int]
|
||||||
|
|
||||||
|
GlyphPosition* = object
|
||||||
|
glyph: string
|
||||||
|
x, y: float
|
||||||
|
minX, maxX: float
|
||||||
|
height: float
|
||||||
|
|
||||||
|
Paint* = NVGPaint
|
||||||
|
Color* = NVGColor
|
||||||
|
Image* = cint
|
||||||
|
Font* = cint
|
||||||
|
|
||||||
|
Winding* {.size: sizeof(cint), pure.} = enum
|
||||||
|
CCW = NVG_CCW
|
||||||
|
CW = NVG_CW
|
||||||
|
|
||||||
|
Solidity* {.size: sizeof(cint), pure.} = enum
|
||||||
|
SOLID = NVG_SOLID
|
||||||
|
HOLE = NVG_HOLE
|
||||||
|
|
||||||
|
TextMetrics* = tuple[ascender, descender, lineHeight: float]
|
||||||
|
|
||||||
|
TextRow* = tuple[x, y, minX, maxX, height: float]
|
||||||
|
|
||||||
|
ImageFlags* {.size: sizeof(cint).} = enum
|
||||||
|
NONE = 0,
|
||||||
|
GENERATE_MIPMAPS = NVG_IMAGE_GENERATE_MIPMAPS, ## Generate mipmaps during creation of the image.
|
||||||
|
REPEATX = NVG_IMAGE_REPEATX, ## Repeat image in X direction.
|
||||||
|
REPEATY = NVG_IMAGE_REPEATY, ## Repeat image in Y direction.
|
||||||
|
FLIPY = NVG_IMAGE_FLIPY, ## Flips (inverses) image in Y direction when rendered.
|
||||||
|
PREMULTIPLIED = NVG_IMAGE_PREMULTIPLIED, ## Image data has premultiplied alpha.
|
||||||
|
NEAREST = NVG_IMAGE_NEAREST ## Image interpolation is Nearest instead Linear
|
||||||
|
|
||||||
|
View* = concept v
|
||||||
|
v.size() is tuple[w, h: int32]
|
||||||
|
v.frameBufferSize() is tuple[w, h: int32]
|
||||||
|
|
||||||
|
proc width*(pos: GlyphPosition): float =
|
||||||
|
return pos.maxX - pos.minX
|
||||||
|
|
||||||
|
proc createExpr*(ty, name: string, descriptor: NimNode, args: varargs[NimNode]): NimNode =
|
||||||
|
let tokens = ($descriptor).toUpper().split(" ").map(
|
||||||
|
proc(s: string): string =
|
||||||
|
return ty & "." & s
|
||||||
|
)
|
||||||
|
|
||||||
|
let argTokens = args.map(
|
||||||
|
proc(arg: NimNode): string =
|
||||||
|
$arg.toStrLit()
|
||||||
|
)
|
||||||
|
|
||||||
|
let allTokens = argTokens.concat(tokens)
|
||||||
|
|
||||||
|
let newExpr = name & "(" & allTokens.join(",") & ")"
|
||||||
|
|
||||||
|
result = parseStmt(newExpr)
|
||||||
|
|
||||||
|
|
||||||
|
template reduce[T](elements: openArray[T], op: untyped): untyped =
|
||||||
|
var res = T.NONE
|
||||||
|
|
||||||
|
for element in elements:
|
||||||
|
res = op(res, element)
|
||||||
|
|
||||||
|
res
|
||||||
|
|
||||||
|
proc createContext(antialias = true, stencilStrokes = true, debugChecks = false): Context =
|
||||||
|
var flags: cint
|
||||||
|
|
||||||
|
if antialias:
|
||||||
|
flags = flags or NVG_ANTIALIAS.cint
|
||||||
|
if stencilStrokes:
|
||||||
|
flags = flags or NVG_STENCIL_STROKES.cint
|
||||||
|
if debugChecks:
|
||||||
|
flags = flags or NVG_DEBUG.cint
|
||||||
|
|
||||||
|
result = CreateGL3(flags)
|
||||||
|
|
||||||
|
if result.isNil:
|
||||||
|
raise newException(ContextInitError, "Could not initialize graphics context")
|
||||||
|
|
||||||
|
var context: Context
|
||||||
|
|
||||||
|
proc init*() =
|
||||||
|
context = createContext()
|
||||||
|
|
||||||
|
proc loadFont*(path, name: string): Font {.discardable.} =
|
||||||
|
## Load the font from `path` and give it a `name` that can be used to reference it.
|
||||||
|
result = context.CreateFont(name, path)
|
||||||
|
|
||||||
|
if result == -1:
|
||||||
|
raise newException(FontLoadError, fmt"Could not load font {name}.")
|
||||||
|
|
||||||
|
proc loadFontMem*(data: openArray[uint8], name: string, freeDataAutomatically = true): Font {.discardable.} =
|
||||||
|
## Load the font from `path` and give it a `name` that can be used to reference it.
|
||||||
|
result = context.CreateFontMem(name, cast[ptr cuchar](data[0].unsafeAddr), data.len.cint, freeDataAutomatically.cint)
|
||||||
|
|
||||||
|
if result == -1:
|
||||||
|
raise newException(FontLoadError, fmt"Could not load font {name}.")
|
||||||
|
|
||||||
|
proc findFont*(name: string): Font =
|
||||||
|
return context.FindFont(name)
|
||||||
|
|
||||||
|
proc setFallbackFont*(baseFont, fallbackFont: string): Font =
|
||||||
|
return context.AddFallbackFont(baseFont, fallbackFont)
|
||||||
|
|
||||||
|
proc setFallbackFont*(baseFont, fallbackFont: Font): Font =
|
||||||
|
return context.AddFallbackFontId(baseFont, fallbackFont)
|
||||||
|
|
||||||
|
proc setFont*(font: Font) =
|
||||||
|
context.FontFaceId(font)
|
||||||
|
|
||||||
|
proc setFont*(name: string) =
|
||||||
|
context.FontFace(name)
|
||||||
|
|
||||||
|
proc setFontSize*(size: float) =
|
||||||
|
context.FontSize(size)
|
||||||
|
|
||||||
|
proc setFontBlur*(blur: float) =
|
||||||
|
context.FontBlur(blur)
|
||||||
|
|
||||||
|
proc setTextLetterSpacing*(spacing: float) =
|
||||||
|
context.TextLetterSpacing(spacing)
|
||||||
|
|
||||||
|
proc setTextLineHeight*(lineHeight: float) =
|
||||||
|
context.TextLineHeight(lineHeight)
|
||||||
|
|
||||||
|
proc `or`(align1: Alignment, align2: Alignment): Alignment =
|
||||||
|
return (align1.cint or align2.cint).Alignment
|
||||||
|
|
||||||
|
proc textAlign*(alignments: varargs[Alignment]) =
|
||||||
|
var alignment = reduce(alignments, `or`)
|
||||||
|
|
||||||
|
context.TextAlign(alignment.cint)
|
||||||
|
|
||||||
|
macro textAlign*(descriptor: string) =
|
||||||
|
result = createExpr("Alignment", "textAlign", descriptor)
|
||||||
|
|
||||||
|
proc textBounds*(x, y: float, text: string): TextBounds =
|
||||||
|
var res: array[4, cfloat]
|
||||||
|
let horizonalAdvance = context.TextBounds(x, y, text, nil, res[0].addr)
|
||||||
|
|
||||||
|
let bounds: Bounds = (res[0].float, res[1].float, res[2].float, res[3].float)
|
||||||
|
return (horizonalAdvance.float, bounds)
|
||||||
|
|
||||||
|
proc text*(x, y: float, content: string) =
|
||||||
|
discard context.Text(x, y, content, nil)
|
||||||
|
|
||||||
|
proc textBoxBounds*(x, y, breakRowWidth: float, text: string): Bounds =
|
||||||
|
var res: array[4, cfloat]
|
||||||
|
context.TextBoxBounds(x, y, breakRowWidth, text, nil, res[0].addr)
|
||||||
|
|
||||||
|
return (res[0].float, res[1].float, res[2].float, res[3].float)
|
||||||
|
|
||||||
|
proc textBox*(x, y, breakRowWidth: float, content: string) =
|
||||||
|
context.TextBox(x, y, breakRowWidth, content, nil)
|
||||||
|
|
||||||
|
proc textMetrics*(): TextMetrics =
|
||||||
|
var
|
||||||
|
ascender, descender, lineHeight: cfloat
|
||||||
|
|
||||||
|
context.TextMetrics(ascender.addr, descender.addr, lineHeight.addr)
|
||||||
|
|
||||||
|
return (ascender.float, descender.float, lineHeight.float)
|
||||||
|
|
||||||
|
# TODO Continue with TextGlyphPos
|
||||||
|
proc glyphPositions*(text: string, x, y: float): seq[GlyphPosition] =
|
||||||
|
const maxPositions = 100
|
||||||
|
var
|
||||||
|
positions: array[maxPositions, NVGglyphPosition]
|
||||||
|
numGlyphs = 0
|
||||||
|
bytesProcessed = 0
|
||||||
|
lastX = x
|
||||||
|
strIndex = 0
|
||||||
|
lineHeight = textMetrics().lineHeight
|
||||||
|
|
||||||
|
result = newSeqOfCap[GlyphPosition](maxPositions)
|
||||||
|
|
||||||
|
while (strIndex < text.len):
|
||||||
|
numGlyphs = context.TextGlyphPositions(
|
||||||
|
lastX, y, cast[cstring](text[strIndex].unsafeAddr), nil,
|
||||||
|
positions[0].addr, maxPositions
|
||||||
|
)
|
||||||
|
|
||||||
|
for i in 0..<numGlyphs:
|
||||||
|
var byteWidth = 1
|
||||||
|
let
|
||||||
|
retPos = positions[i]
|
||||||
|
if i < (numGlyphs - 1):
|
||||||
|
let
|
||||||
|
nextPos = positions[i+1]
|
||||||
|
nextBaddr = cast[ByteAddress](nextPos.str[0].unsafeAddr)
|
||||||
|
baddr = cast[ByteAddress](retPos.str[0].unsafeAddr)
|
||||||
|
|
||||||
|
byteWidth = nextBaddr - baddr
|
||||||
|
elif i == (numGlyphs - 1):
|
||||||
|
byteWidth = len(text) - bytesProcessed
|
||||||
|
|
||||||
|
var glyph = newStringOfCap(byteWidth)
|
||||||
|
for j in 0..<byteWidth:
|
||||||
|
glyph.add(retPos.str[j])
|
||||||
|
|
||||||
|
let pos = GlyphPosition(
|
||||||
|
glyph: glyph, x: retPos.x, y: y, minX: retPos.minX, maxX: retPos.maxX,
|
||||||
|
height: lineHeight
|
||||||
|
)
|
||||||
|
|
||||||
|
result.add(pos)
|
||||||
|
bytesProcessed += byteWidth
|
||||||
|
|
||||||
|
lastX = positions[numGlyphs-1].x
|
||||||
|
strIndex += maxPositions
|
||||||
|
|
||||||
|
proc glyphPositions*(text: string, x, y, maxWidth: float): seq[GlyphPosition] =
|
||||||
|
const maxRows = 100
|
||||||
|
var
|
||||||
|
rows: array[maxRows, NVGtextRow]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
proc beginFrame*(width, height, pxRatio: float) =
|
||||||
|
context.BeginFrame(width, height, pxRatio)
|
||||||
|
|
||||||
|
proc cancelFrame*() =
|
||||||
|
context.CancelFrame()
|
||||||
|
|
||||||
|
proc endFrame*() =
|
||||||
|
context.EndFrame()
|
||||||
|
|
||||||
|
proc globalCompositeOperation*(op: CompositeOperation) =
|
||||||
|
context.GlobalCompositeOperation(op.cint)
|
||||||
|
|
||||||
|
proc globalCompositeBlendFunc*(sourceFactor, destFactor: BlendFactor) =
|
||||||
|
context.GlobalCompositeBlendFunc(sourceFactor.cint, destFactor.cint)
|
||||||
|
|
||||||
|
proc globalCompositeBlendFuncSeparate*(srcRGB, destRGB, srcAlpha, destAlpha: BlendFactor) =
|
||||||
|
context.GlobalCompositeBlendFuncSeparate(srcRGB.cint, destRGB.cint, srcAlpha.cint, destAlpha.cint)
|
||||||
|
|
||||||
|
template withFrame*(window: View, code: untyped) =
|
||||||
|
let
|
||||||
|
winSize = window.size()
|
||||||
|
fbSize = window.frameBufferSize()
|
||||||
|
pxRatio = cfloat(fbSize.w) / cfloat(winSize.w)
|
||||||
|
|
||||||
|
beginFrame(winSize.w.cfloat, winSize.h.cfloat, pxRatio)
|
||||||
|
code
|
||||||
|
endFrame()
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# State Handling
|
||||||
|
#
|
||||||
|
# NanoVG contains state which represents how paths will be rendered.
|
||||||
|
# The state contains transform, fill and stroke styles, text and font styles,
|
||||||
|
# and scissor clipping.
|
||||||
|
#
|
||||||
|
proc saveState*() =
|
||||||
|
## Pushes and saves the current render state into a state stack.
|
||||||
|
## A matching restoreState() must be used to restore the state.
|
||||||
|
context.Save()
|
||||||
|
|
||||||
|
proc restoreState*() =
|
||||||
|
## Pops and restores current render state.
|
||||||
|
context.Restore()
|
||||||
|
|
||||||
|
proc resetState*() =
|
||||||
|
## Resets current render state to default values. Does not affect the render state stack.
|
||||||
|
context.Reset()
|
||||||
|
|
||||||
|
template withState*(code: untyped) =
|
||||||
|
saveState()
|
||||||
|
try:
|
||||||
|
code
|
||||||
|
except:
|
||||||
|
raise
|
||||||
|
finally:
|
||||||
|
restoreState()
|
||||||
|
|
||||||
|
proc setShapeAntialias*(enabled = true) =
|
||||||
|
context.ShapeAntiAlias(enabled.cint)
|
||||||
|
|
||||||
|
proc setStrokeColor*(color: Color) =
|
||||||
|
context.StrokeColor(color)
|
||||||
|
|
||||||
|
proc setStrokePaint*(paint: Paint) =
|
||||||
|
context.StrokePaint(paint)
|
||||||
|
|
||||||
|
proc setFillPaint*(paint: Paint) =
|
||||||
|
context.FillPaint(paint)
|
||||||
|
|
||||||
|
proc setFillColor*(color: Color) =
|
||||||
|
context.FillColor(color)
|
||||||
|
|
||||||
|
proc setFillColor*(r, g, b: uint8) =
|
||||||
|
context.FillColor(RGB(r.cuchar, g.cuchar, b.cuchar))
|
||||||
|
|
||||||
|
proc setFillColor*(r, g, b: float) =
|
||||||
|
context.FillColor(RGBf(r, g, b))
|
||||||
|
|
||||||
|
proc setFillColor*(r, g, b, a: float) =
|
||||||
|
context.FillColor(RGBAf(r, g, b, a))
|
||||||
|
|
||||||
|
proc setFillColor*(r, g, b, a: uint8) =
|
||||||
|
context.FillColor(RGBA(r.cuchar, g.cuchar, b.cuchar, a.cuchar))
|
||||||
|
|
||||||
|
proc setMiterLimit*(limit: float) =
|
||||||
|
context.MiterLimit(limit)
|
||||||
|
|
||||||
|
proc setStrokeWidth*(size: float) =
|
||||||
|
context.StrokeWidth(size)
|
||||||
|
|
||||||
|
proc setLineCap*(cap: LineCap) =
|
||||||
|
context.LineCap(cap.cint)
|
||||||
|
|
||||||
|
proc setLineJoin*(join: LineJoin) =
|
||||||
|
context.LineJoin(join.cint)
|
||||||
|
|
||||||
|
proc setGlobalAlpha*(alpha: float) =
|
||||||
|
context.GlobalAlpha(alpha)
|
||||||
|
|
||||||
|
# Transforms
|
||||||
|
|
||||||
|
proc resetTransform*() =
|
||||||
|
context.ResetTransform()
|
||||||
|
|
||||||
|
proc transform*(a, b, c, d, e, f: float) =
|
||||||
|
context.Transform(a, b, c, d, e, f)
|
||||||
|
|
||||||
|
proc translate*(x, y: float) =
|
||||||
|
context.Translate(x, y)
|
||||||
|
|
||||||
|
proc rotate*(angle: float) =
|
||||||
|
context.Rotate(angle)
|
||||||
|
|
||||||
|
proc skewX*(angle: float) =
|
||||||
|
context.SkewX(angle)
|
||||||
|
|
||||||
|
proc skewY*(angle: float) =
|
||||||
|
context.SkewY(angle)
|
||||||
|
|
||||||
|
proc scale*(x, y: float) =
|
||||||
|
context.Scale(x, y)
|
||||||
|
|
||||||
|
proc copyArray[T, U](src: T): U =
|
||||||
|
for i in 0..<src.len:
|
||||||
|
result[i] = type(result[i])((src[i]))
|
||||||
|
|
||||||
|
proc getCurrentTransform*(): array[6, float] =
|
||||||
|
var res: array[6, cfloat]
|
||||||
|
context.CurrentTransform(res[0].addr)
|
||||||
|
return copyArray[array[6, cfloat], array[6, float]](res)
|
||||||
|
|
||||||
|
proc setTransformIdentity*(dest: array[6, float]) =
|
||||||
|
var dst = copyArray[array[6, float], array[6, cfloat]](dest)
|
||||||
|
TransformIdentity(dst[0].addr)
|
||||||
|
|
||||||
|
|
||||||
|
proc degreesToRadians*(degrees: float): float =
|
||||||
|
return DegToRad(degrees)
|
||||||
|
|
||||||
|
proc radiansToDegrees*(radians: float): float =
|
||||||
|
return RadToDeg(radians)
|
||||||
|
|
||||||
|
# Images
|
||||||
|
|
||||||
|
proc `or`(imageflags1: ImageFlags, imageflags2: ImageFlags): ImageFlags =
|
||||||
|
return (imageflags1.cint or imageflags2.cint).ImageFlags
|
||||||
|
|
||||||
|
proc loadImage*(fileName: string, flags: varargs[ImageFlags]): Image =
|
||||||
|
var flagArgs = reduce(flags, `or`)
|
||||||
|
result = context.CreateImage(fileName, flagArgs.cint)
|
||||||
|
|
||||||
|
if result == -1:
|
||||||
|
raise newException(ImageLoadError, fmt"Could not load image {fileName}.")
|
||||||
|
|
||||||
|
macro loadImage*(fileName, flags: string): Image =
|
||||||
|
return createExpr("ImageFlags", "loadImage", flags, fileName)
|
||||||
|
|
||||||
|
proc createImageFromArray*(data: openArray[uint8], flags: varargs[ImageFlags]): Image =
|
||||||
|
var flagArgs = reduce(flags, `or`)
|
||||||
|
result = context.CreateImageMem(flagArgs.cint, cast[ptr cuchar](data[0].unsafeAddr), data.len.cint)
|
||||||
|
|
||||||
|
if result == -1:
|
||||||
|
raise newException(ImageLoadError, fmt"Could not load image from data.")
|
||||||
|
|
||||||
|
proc size*(image: Image): Size =
|
||||||
|
var rw, rh: cint
|
||||||
|
context.ImageSize(image, rw.addr, rh.addr)
|
||||||
|
result.width = rw
|
||||||
|
result.height = rh
|
||||||
|
|
||||||
|
proc delete*(image: Image) =
|
||||||
|
context.DeleteImage(image)
|
||||||
|
|
||||||
|
# Paths
|
||||||
|
proc beginPath*() =
|
||||||
|
context.BeginPath()
|
||||||
|
|
||||||
|
proc pathMoveTo*(x, y: float) =
|
||||||
|
context.MoveTo(x, y)
|
||||||
|
|
||||||
|
proc pathLineTo*(x, y: float) =
|
||||||
|
context.LineTo(x, y)
|
||||||
|
|
||||||
|
proc pathBezierTo*(c1x, c1y, c2x, c2y, x, y: float) =
|
||||||
|
context.BezierTo(c1x, c1y, c2x, c2y, x, y)
|
||||||
|
|
||||||
|
proc pathQuadTo*(cx, cy, x, y: float) =
|
||||||
|
context.QuadTo(cx, cy, x, y)
|
||||||
|
|
||||||
|
proc pathArcTo*(x1, y1, x2, y2, radius: float) =
|
||||||
|
context.ArcTo(x1, y1, x2, y2, radius)
|
||||||
|
|
||||||
|
proc closePath*() =
|
||||||
|
context.ClosePath()
|
||||||
|
|
||||||
|
proc pathWinding*(dir: Winding) =
|
||||||
|
context.PathWinding(dir.cint)
|
||||||
|
|
||||||
|
proc setSolidity*(solidity: Solidity) =
|
||||||
|
context.PathWinding(solidity.cint)
|
||||||
|
|
||||||
|
proc pathArc*(cx, cy, r, a0, a1: float, dir: int) =
|
||||||
|
context.Arc(cx, cy, r, a0, a1, dir.cint)
|
||||||
|
|
||||||
|
proc pathRect*(x, y, width, height: float) =
|
||||||
|
context.Rect(x, y, width, height)
|
||||||
|
|
||||||
|
proc pathRoundedRect*(x, y, width, height, radius: float) =
|
||||||
|
context.RoundedRect(x, y, width, height, radius)
|
||||||
|
|
||||||
|
proc pathRoundedRectVarying*(x, y, width, height, radiusTopLeft, radiusTopRight,
|
||||||
|
radiusBottomRight, radiusBottomLeft: float) =
|
||||||
|
context.RoundedRectVarying(
|
||||||
|
x, y, width, height,
|
||||||
|
radiusTopLeft, radiusTopRight, radiusBottomRight, radiusBottomLeft
|
||||||
|
)
|
||||||
|
|
||||||
|
proc pathEllipse*(cx, cy, radiusX, radiusY: float) =
|
||||||
|
context.Ellipse(cx, cy, radiusX, radiusY)
|
||||||
|
|
||||||
|
proc pathCircle*(cx, cy, radius: float) =
|
||||||
|
context.Circle(cx, cy, radius)
|
||||||
|
|
||||||
|
proc pathFill*() =
|
||||||
|
context.Fill()
|
||||||
|
|
||||||
|
proc pathStroke*() =
|
||||||
|
context.Stroke()
|
||||||
|
|
||||||
|
proc linearGradient*(startX, startY, endX, endY: float, startColor, endColor: Color): Paint =
|
||||||
|
result = context.LinearGradient(startX, startY, endX, endY, startColor, endColor)
|
||||||
|
|
||||||
|
proc boxGradient*(x, y, width, height, radius, feather: float, startColor, endColor: Color): Paint =
|
||||||
|
result = context.BoxGradient(x, y, width, height, radius, feather, startColor, endColor)
|
||||||
|
|
||||||
|
proc radialGradient*(centerX, centerY, innerRadius, outerRadius: float, startColor, endColor: Color): Paint =
|
||||||
|
result = context.RadialGradient(centerX, centerY, innerRadius, outerRadius, startColor, endColor)
|
||||||
|
|
||||||
|
proc imagePattern*(originX, originY, renderSizeX, renderSizeY, angle: float, image: Image, alpha: float): Paint =
|
||||||
|
result = context.ImagePattern(originX, originY, renderSizeX, renderSizeY, angle, image, alpha)
|
||||||
|
|
||||||
|
proc setScissor*(x, y, width, height: float) =
|
||||||
|
context.Scissor(x, y, width, height)
|
||||||
|
|
||||||
|
proc intersectScissor*(x, y, width, height: float) =
|
||||||
|
context.IntersectScissor(x, y, width, height)
|
||||||
|
|
||||||
|
proc resetScissor*() =
|
||||||
|
context.ResetScissor()
|
||||||
|
|
||||||
|
proc cleanup() {.noconv.} =
|
||||||
|
echo "running cleanup"
|
||||||
|
context.DeleteGL3()
|
||||||
|
|
||||||
|
addQuitProc(cleanup)
|
||||||
97
src/nanovg/wrapper.nim
Normal file
97
src/nanovg/wrapper.nim
Normal file
|
|
@ -0,0 +1,97 @@
|
||||||
|
import os
|
||||||
|
import opengl
|
||||||
|
import strutils
|
||||||
|
|
||||||
|
import nimterop/[cimport, build]
|
||||||
|
|
||||||
|
# Documentation:
|
||||||
|
# https://github.com/nimterop/nimterop
|
||||||
|
# https://nimterop.github.io/nimterop/cimport.html
|
||||||
|
|
||||||
|
const
|
||||||
|
# Location where any sources should get downloaded. Adjust depending on
|
||||||
|
# actual location of wrapper file relative to project.
|
||||||
|
baseDir = currentSourcePath.parentDir().parentDir().parentDir()/"build"
|
||||||
|
|
||||||
|
# All files and dirs should be inside to baseDir
|
||||||
|
srcDir = baseDir/"nanovg"
|
||||||
|
|
||||||
|
static:
|
||||||
|
# Print generated Nim to output
|
||||||
|
# cDebug()
|
||||||
|
|
||||||
|
# Disable caching so that wrapper is generated every time. Useful during
|
||||||
|
# development. Remove once wrapper is working as expected.
|
||||||
|
# cDisableCaching()
|
||||||
|
|
||||||
|
# Download C/C++ source code from a git repository
|
||||||
|
gitPull("https://github.com/memononen/nanovg", outdir=srcDir, plist="""
|
||||||
|
src/*.h
|
||||||
|
src/*.c
|
||||||
|
""", checkout = "1f9c8864fc556a1be4d4bf1d6bfe20cde25734b4")
|
||||||
|
|
||||||
|
let contents = readFile(srcDir/"src/nanovg_gl.h")
|
||||||
|
let newContents = contents.replace("#define NANOVG_GL_H\n", """
|
||||||
|
#define NANOVG_GL_USE_UNIFORMBUFFER
|
||||||
|
#define NANOVG_GL3
|
||||||
|
#define GLFW_INCLUDE_GLEXT
|
||||||
|
#ifdef __APPLE__
|
||||||
|
# define GLFW_INCLUDE_GLCOREARB
|
||||||
|
#endif
|
||||||
|
#include <GLFW/glfw3.h>
|
||||||
|
#include "nanovg.h"
|
||||||
|
""")
|
||||||
|
writeFile(srcDir/"src/nanovg_gl.h", newContents)
|
||||||
|
|
||||||
|
# Manually wrap any symbols since nimterop cannot or incorrectly wraps them
|
||||||
|
cOverride:
|
||||||
|
# Standard Nim code to wrap types, consts, procs, etc.
|
||||||
|
type
|
||||||
|
NVGcolor* {.importc: "struct NVGcolor", bycopy.} = object
|
||||||
|
r* {.importc: "r".}: cfloat
|
||||||
|
g* {.importc: "g".}: cfloat
|
||||||
|
b* {.importc: "b".}: cfloat
|
||||||
|
a* {.importc: "a".}: cfloat
|
||||||
|
|
||||||
|
# Specify include directories for gcc and Nim
|
||||||
|
cIncludeDir(srcDir/"src")
|
||||||
|
|
||||||
|
# Define global symbols
|
||||||
|
cDefine("NANOVG_GL3_IMPLEMENTATION", "1")
|
||||||
|
|
||||||
|
# Any global compiler options
|
||||||
|
when defined(macosx):
|
||||||
|
# cDefine("GLFW_INCLUDE_GLCOREARB", "1")
|
||||||
|
{.passC: "-DNANOVG_GL3_IMPLEMENTATION -DNANOVG_GLEW -D_GLFW_USE_RETINA -D_GLFW_COCOA -D_GLFW_USE_CHDir -D_GLFW_USE_MENUBAR".}
|
||||||
|
{.passL: "-m64 -framework GLUT -framework OpenGL -framework Cocoa -framework IOKit -framework CoreVideo -framework Carbon -lm".}
|
||||||
|
|
||||||
|
elif defined(unix):
|
||||||
|
{.passC: "-DNANOVG_GL3_IMPLEMENTATION -DNANOVG_GLEW".}
|
||||||
|
{.passL: "-lGL -lGLU -lGLEW -lm -lglfw".}
|
||||||
|
|
||||||
|
|
||||||
|
# Compile in any common source code
|
||||||
|
cCompile(srcDir/"src/*.c")
|
||||||
|
|
||||||
|
# Use cPlugin() to make any symbol changes
|
||||||
|
cPlugin:
|
||||||
|
import strutils
|
||||||
|
|
||||||
|
# Symbol renaming examples
|
||||||
|
proc onSymbol*(sym: var Symbol) {.exportc, dynlib.} =
|
||||||
|
# Get rid of leading and trailing underscores
|
||||||
|
# sym.name = sym.name.strip(chars = {'_'})
|
||||||
|
|
||||||
|
# Remove prefixes or suffixes from procs
|
||||||
|
if sym.kind == nskProc:
|
||||||
|
sym.name = sym.name.replace("glnvg__", "")
|
||||||
|
sym.name = sym.name.replace("nvg__", "")
|
||||||
|
sym.name = sym.name.replace("nvglu", "")
|
||||||
|
sym.name = sym.name.replace("nvgl", "")
|
||||||
|
sym.name = sym.name.replace("nvg", "")
|
||||||
|
|
||||||
|
# Finally import wrapped header file. Recurse if #include files should also
|
||||||
|
# be wrapped. Set dynlib if binding to dynamic library.
|
||||||
|
cImport(srcDir/"src/nanovg.h")
|
||||||
|
cImport(srcDir/"src/nanovg_gl.h")
|
||||||
|
cImport(srcDir/"src/nanovg_gl_utils.h")
|
||||||
Loading…
Add table
Add a link
Reference in a new issue