From 77e390e9be1ae3a63832f5766053881e5a9e50fd Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Sat, 25 Apr 2020 22:07:47 -0600 Subject: [PATCH] Use local context instead of global --- examples/common.nim | 140 +++++++++--------- examples/demo.nim | 27 ++-- examples/demo_sdl2.nim | 29 ++-- examples/demo_sdl2_gpu.nim | 29 ++-- examples/perf.nim | 56 +++---- src/nanovg.nim | 295 ++++++++++++++++++++----------------- 6 files changed, 302 insertions(+), 274 deletions(-) diff --git a/examples/common.nim b/examples/common.nim index b470b0f..3b6ff7b 100644 --- a/examples/common.nim +++ b/examples/common.nim @@ -18,34 +18,34 @@ proc currentSourceDir*(): string {.inline.} = # 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) +proc drawLabel*(vg: Context, text: string, x, y, w, h: float) = + vg.setFontSize(50.0) + vg.setFont("sans") + vg.setFillColor(1.0, 1.0, 1.0, 1.0) - nanovg.textAlign("left middle") - nanovg.text(text, x, y+h*0.5) + vg.textAlign("left middle") + vg.text(text, x, y+h*0.5) -proc drawWindow*(title: string, x, y, w, h: float) = +proc drawWindow*(vg: Context, 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() + withState(vg): + vg.beginPath() + vg.pathRoundedRect(x, y, w, h, cornerRadius) + #vg.setFillColor(rgba(255,255,255,192)) + vg.setFillColor(rgba(28,30,34,192)) + vg.pathFill() - nanovg.setFontSize(30.0) - nanovg.setFont("sans") - nanovg.textAlign("center middle") + vg.setFontSize(30.0) + vg.setFont("sans") + vg.textAlign("center middle") - nanovg.setFontBlur(2) - nanovg.setFillColor(rgba(0,0,0,128)) - nanovg.text(title, x+w/2, y+16+1) + vg.setFontBlur(2) + vg.setFillColor(rgba(0,0,0,128)) + vg.text(title, x+w/2, y+16+1) - nanovg.setFontBlur(0) - nanovg.setFillColor(rgba(220,220,220,160)) - nanovg.text(title, x+w/2,y+16) + vg.setFontBlur(0) + vg.setFillColor(rgba(220,220,220,160)) + vg.text(title, x+w/2,y+16) proc isBlack*(color: Color): bool = result = true @@ -93,8 +93,8 @@ proc cpToUtf8*(code: int): string = return res -proc drawButton*(text: string, x, y, width, height: float) = - let bg = linearGradient( +proc drawButton*(vg: Context, text: string, x, y, width, height: float) = + let bg = vg.linearGradient( x, y, x, y+height, rgba(255, 255, 255, 16), rgba(100, 100, 100, 16) @@ -109,28 +109,28 @@ proc drawButton*(text: string, x, y, width, height: float) = 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() + vg.beginPath() + vg.pathRoundedRect(x+1+shadowOffset, y+1+shadowOffset, width-2, height-2, radius) + vg.setFillColor(shadowColor) + vg.pathFill() - nanovg.beginPath() - nanovg.pathRoundedRect(x+1, y+1, width-2, height-2, radius) - nanovg.setFillColor(color) - nanovg.pathFill() + vg.beginPath() + vg.pathRoundedRect(x+1, y+1, width-2, height-2, radius) + vg.setFillColor(color) + vg.pathFill() - nanovg.setFillPaint(bg) - nanovg.pathFill() + vg.setFillPaint(bg) + vg.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() + vg.beginPath() + vg.pathMoveTo(x+radius, y+lightWidth+1) + vg.setStrokeWidth(lightWidth) + vg.setStrokeColor(highlightColor) + vg.pathLineTo(x+width-(radius), y+lightWidth+1) + vg.pathStroke() -proc drawButton*(preicon: string, text: string, x, y, w, h: float, color: Color) = +proc drawButton*(vg: Context, preicon: string, text: string, x, y, w, h: float, color: Color) = var bg: Paint @@ -140,50 +140,50 @@ proc drawButton*(preicon: string, text: string, x, y, w, h: float, color: Color) tw = 0.0 iw = 0.0 - bg = nanovg.linearGradient( + bg = vg.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) + vg.beginPath() + vg.pathRoundedRect(x+1, y+1, w-2, h-2, cornerRadius-1) if not isBlack(color): - nanovg.setFillColor(color) - nanovg.pathFill() + vg.setFillColor(color) + vg.pathFill() - nanovg.setFillPaint(bg) - nanovg.pathFill() + vg.setFillPaint(bg) + vg.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() + vg.beginPath() + vg.pathRoundedRect(x+0.5, y+0.5, w-1, h-1, cornerRadius-0.5) + vg.setStrokeColor(rgba(0,0,0,48)) + vg.pathStroke() - nanovg.setFontSize(20.0) - nanovg.setFont("sans-bold") - tb = nanovg.textBounds(text, 0.0, 0.0) + vg.setFontSize(20.0) + vg.setFont("sans-bold") + tb = vg.textBounds(text, 0.0, 0.0) tw = tb.horizontalAdvance - nanovg.setFontSize(h*0.5) - nanovg.setFont("fa") - ib = nanovg.textBounds(preicon, 0, 0) + vg.setFontSize(h*0.5) + vg.setFont("fa") + ib = vg.textBounds(preicon, 0, 0) 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(preicon, x+w*0.5-tw*0.5-iw*0.75, y+h*0.5) + vg.setFontSize(h*0.5) + vg.setFont("fa") + vg.setFillColor(rgba(255,255,255,96)) + vg.textAlign("left middle") + vg.text(preicon, x+w*0.5-tw*0.5-iw*0.75, y+h*0.5) - nanovg.setFontSize(20.0) - nanovg.setFont("sans-bold") - nanovg.textAlign("left middle") - nanovg.setFillColor(rgba(0,0,0,160)) - nanovg.text(text, x+w*0.5-tw*0.5+iw*0.25, y+h*0.5-1) - nanovg.setFillColor(rgba(255,255,255,160)) - nanovg.text(text, x+w*0.5-tw*0.5+iw*0.25, y+h*0.5) + vg.setFontSize(20.0) + vg.setFont("sans-bold") + vg.textAlign("left middle") + vg.setFillColor(rgba(0,0,0,160)) + vg.text(text, x+w*0.5-tw*0.5+iw*0.25, y+h*0.5-1) + vg.setFillColor(rgba(255,255,255,160)) + vg.text(text, x+w*0.5-tw*0.5+iw*0.25, y+h*0.5) proc glewInit*() {.header:"".} \ No newline at end of file diff --git a/examples/demo.nim b/examples/demo.nim index e676ee7..5e68a4c 100644 --- a/examples/demo.nim +++ b/examples/demo.nim @@ -23,10 +23,10 @@ proc main = setTime(0) glewInit() - 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") + let vg = nanovg.newContext() + vg.loadFont(currentSourceDir()/"resources/Roboto-Regular.ttf", "sans") + vg.loadFont(currentSourceDir()/"resources/Roboto-Bold.ttf", "sans-bold") + vg.loadFont(currentSourceDir()/"resources/fa.ttf", "fa") var prevt = getTime() while not w.shouldClose(): @@ -43,16 +43,16 @@ proc main = 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) + vg.withFrame(w): + let x = vg.glyphPositions("Some仮名thing is up", 0, 0) + vg.drawWindow("Title", 50, 50, 300, 400) + vg.drawLabel("Hello!", 10, 10, 280, 20) + vg.drawButton(ICON_TRASH, "Delete", 100, 200, 160, 28, rgba(128, 16, 8, 255)) + vg.drawButton(ICON_TRASH, "Deleter", 100, 250, 160, 28, rgba(12, 130, 80, 255)) + vg.drawButton("Testing", 100, 300, 160, 28) - fps.render(5, 5) - cpuGraph.render(210, 5) + vg.render(fps, 5, 5) + vg.render(cpuGraph, 210, 5) w.swapBuffers() pollEvents() @@ -64,6 +64,7 @@ proc main = waitEvents() w.destroy() + vg.delete() glfw.terminate() main() diff --git a/examples/demo_sdl2.nim b/examples/demo_sdl2.nim index 35d6569..2dcb362 100644 --- a/examples/demo_sdl2.nim +++ b/examples/demo_sdl2.nim @@ -71,11 +71,11 @@ proc main() = var prevt: float = sdl.getTicks().float/1000.0 glewInit() - 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") + let vg = nanovg.newContext() + vg.loadFont(currentSourceDir()/"resources/Roboto-Regular.ttf", "sans") + vg.loadFont(currentSourceDir()/"resources/Roboto-Bold.ttf", "sans-bold") + vg.loadFont(currentSourceDir()/"resources/fa.ttf", "fa") + vg.loadFont(currentSourceDir()/"resources/entypo.ttf", "icons") while not done: let fbSize = window.frameBufferSize() @@ -86,16 +86,16 @@ proc main() = 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(window): - 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) + vg.withFrame(window): + let x = glyphPositions(vg, "Some仮名thing is up", 0, 0) + vg.drawWindow("Title", 50, 50, 300, 400) + vg.drawLabel("Hello!", 10, 10, 280, 20) + vg.drawButton(ICON_TRASH, "Delete", 100, 200, 160, 28, rgba(128, 16, 8, 255)) + vg.drawButton(ICON_TRASH, "Deleter", 100, 250, 160, 28, rgba(12, 130, 80, 255)) + vg.drawButton("Testing", 100, 300, 160, 28) - fps.render(5, 5) - cpuGraph.render(210, 5) + vg.render(fps, 5, 5) + vg.render(cpuGraph, 210, 5) let t: float = sdl.getTicks().float/1000.0 let dt = t - prevt @@ -112,6 +112,7 @@ proc main() = sdl.glDeleteContext(context) window.destroyWindow() + vg.delete() img.quit() sdl.quit() diff --git a/examples/demo_sdl2_gpu.nim b/examples/demo_sdl2_gpu.nim index e717315..05251c7 100644 --- a/examples/demo_sdl2_gpu.nim +++ b/examples/demo_sdl2_gpu.nim @@ -91,12 +91,12 @@ proc main() = gpu.setRequiredFeatures(gpu.FEATURE_BASIC_SHADERS) - nanovg.init() + let vg = nanovg.newContext() discard sdl.glSetSwapInterval(0) - 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") + vg.loadFont(currentSourceDir()/"resources/umeboshi.ttf", "sans") + vg.loadFont(currentSourceDir()/"resources/Roboto-Bold.ttf", "sans-bold") + vg.loadFont(currentSourceDir()/"resources/fa.ttf", "fa") + vg.loadFont(currentSourceDir()/"resources/entypo.ttf", "icons") var done = false var prevt: float = sdl.getTicks().float/1000.0 @@ -122,18 +122,18 @@ proc main() = gpu.flushBlitBuffer() - withFrame(window): - text("Some仮名thing is up", 100, 100, 100) - drawWindow("Title", 50, 50, 300, 400) - drawLabel("Hello!", 10, 10, 500, 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) + withFrame(vg, window): + vg.text("Some仮名thing is up", 100, 100, 100) + vg.drawWindow("Title", 50, 50, 300, 400) + vg.drawLabel("Hello!", 10, 10, 500, 20) + vg.drawButton(ICON_TRASH, "Delete", 100, 200, 160, 28, rgba(128, 16, 8, 255)) + vg.drawButton(ICON_TRASH, "Deleter", 100, 250, 160, 28, rgba(12, 130, 80, 255)) + vg.drawButton("Testing", 100, 300, 160, 28) gpu.resetRendererState() - fps.render(5, 5) - cpuGraph.render(210, 5) + vg.render(fps, 5, 5) + vg.render(cpuGraph, 210, 5) gpu.flip(screen) @@ -149,6 +149,7 @@ proc main() = done = events() gpu.freeImage(image) + vg.delete() gpu.quit() main() \ No newline at end of file diff --git a/examples/perf.nim b/examples/perf.nim index 8f0ef8c..1024694 100644 --- a/examples/perf.nim +++ b/examples/perf.nim @@ -32,19 +32,19 @@ proc getGraphAverage(graph: PerfGraph): float = return avg / GRAPH_HISTORY_COUNT.float -proc render*(graph: PerfGraph, x, y: float) = +proc render*(vg: Context, 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() + vg.beginPath() + vg.pathRect(x, y, width, height) + vg.setFillColor(0, 0, 0, 128) + vg.pathFill() - nanovg.beginPath() - nanovg.pathMoveTo(x, y+height) + vg.beginPath() + vg.pathMoveTo(x, y+height) case graph.style: of RenderStyle.FPS: @@ -58,7 +58,7 @@ proc render*(graph: PerfGraph, x, y: float) = vx = x + (i/(GRAPH_HISTORY_COUNT-1)) * width vy = y + height - ((v / 80) * height) - nanovg.pathLineTo(vx, vy) + vg.pathLineTo(vx, vy) of RenderStyle.PERCENT: for i in 0 ..< GRAPH_HISTORY_COUNT: @@ -71,7 +71,7 @@ proc render*(graph: PerfGraph, x, y: float) = vx = x + (i/(GRAPH_HISTORY_COUNT-1)) * width vy = y + height - ((v / 100) * height) - nanovg.pathLineTo(vx, vy) + vg.pathLineTo(vx, vy) of RenderStyle.MS: for i in 0 ..< GRAPH_HISTORY_COUNT: @@ -84,40 +84,40 @@ proc render*(graph: PerfGraph, x, y: float) = vx = x + (i/(GRAPH_HISTORY_COUNT-1)) * width vy = y + height - ((v / 20) * height) - nanovg.pathLineTo(vx, vy) + vg.pathLineTo(vx, vy) - nanovg.pathLineTo(x+width, y+height) - nanovg.setFillColor(255, 192, 0, 128) - nanovg.pathFill() + vg.pathLineTo(x+width, y+height) + vg.setFillColor(255, 192, 0, 128) + vg.pathFill() - nanovg.setFont("sans") + vg.setFont("sans") if graph.name.len() > 0: - nanovg.setFontSize(14) - nanovg.textAlign("top left") - nanovg.setFillColor(240, 240, 240, 192) - nanovg.text(graph.name, x+3, y+1) + vg.setFontSize(14) + vg.textAlign("top left") + vg.setFillColor(240, 240, 240, 192) + vg.text(graph.name, x+3, y+1) - nanovg.setFontSize(18) - nanovg.textAlign("top right") - nanovg.setFillColor(240, 240, 240, 255) + vg.setFontSize(18) + vg.textAlign("top right") + vg.setFillColor(240, 240, 240, 255) case graph.style: of RenderStyle.FPS: var str = fmt"{1.0/avg:.2f} FPS" - nanovg.text(str, x+width-3, y+1) - nanovg.setFontSize(15) - nanovg.textAlign("bottom right") - nanovg.setFillColor(240, 240, 240, 160) + vg.text(str, x+width-3, y+1) + vg.setFontSize(15) + vg.textAlign("bottom right") + vg.setFillColor(240, 240, 240, 160) str = fmt"{avg*1000:.2f} ms" - nanovg.text(str, x+width-3, y+height-1) + vg.text(str, x+width-3, y+height-1) of RenderStyle.PERCENT: let str = fmt"{avg:.1f} %" - nanovg.text(str, x+width-3, y+1) + vg.text(str, x+width-3, y+1) of RenderStyle.MS: let str = fmt"{avg*1000:.2f} ms" - nanovg.text(str, x+width-3, y+1) + vg.text(str, x+width-3, y+1) diff --git a/src/nanovg.nim b/src/nanovg.nim index 3ddc968..2cb3232 100644 --- a/src/nanovg.nim +++ b/src/nanovg.nim @@ -24,7 +24,7 @@ type FontLoadError* = object of CatchableError ImageLoadError* = object of CatchableError ContextInitError* = object of CatchableError - Context = ptr NVGContext + Context* = ptr NVGContext Alignment* {.size: sizeof(cint), pure.} = enum NONE = 0 @@ -114,7 +114,7 @@ type proc width*(pos: GlyphPosition): float = return pos.maxX - pos.minX -proc createExpr*(ty, name: string, descriptor: NimNode, args: varargs[NimNode]): NimNode = +proc createExpr*(context: NimNode, ty, name: string, descriptor: NimNode, args: varargs[NimNode]): NimNode = let tokens = ($descriptor).toUpper().split(" ").map( proc(s: string): string = return ty & "." & s @@ -127,7 +127,7 @@ proc createExpr*(ty, name: string, descriptor: NimNode, args: varargs[NimNode]): let allTokens = argTokens.concat(tokens) - let newExpr = name & "(" & allTokens.join(",") & ")" + let newExpr = name & "(" & $context.toStrLit & ", " & allTokens.join(",") & ")" result = parseStmt(newExpr) @@ -140,7 +140,10 @@ template reduce[T](elements: openArray[T], op: untyped): untyped = res -proc createContext(antialias = true, stencilStrokes = true, debugChecks = false): Context = +proc delete*(context: Context) = + context.deleteGL3() + +proc newContext*(antialias = true, stencilStrokes = true, debugChecks = false): Context = var flags: cint if antialias: @@ -155,83 +158,78 @@ proc createContext(antialias = true, stencilStrokes = true, debugChecks = false) 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.} = +proc loadFont*(context: Context, 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.} = +proc loadFontMem*(context: Context, 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 = +proc findFont*(context: Context, name: string): Font = return context.findFont(name) -proc setFallbackFont*(baseFont, fallbackFont: string): Font = +proc setFallbackFont*(context: Context, baseFont, fallbackFont: string): Font = return context.addFallbackFont(baseFont, fallbackFont) -proc setFallbackFont*(baseFont, fallbackFont: Font): Font = +proc setFallbackFont*(context: Context, baseFont, fallbackFont: Font): Font = return context.addFallbackFontId(baseFont, fallbackFont) -proc setFont*(font: Font) = +proc `font=`*(context: Context, font: Font) = context.fontFaceId(font) -proc setFont*(name: string) = +proc setFont*(context: Context, name: string) = context.fontFace(name) -proc setFontSize*(size: float) = +proc setFontSize*(context: Context, size: float) = context.fontSize(size) -proc setFontBlur*(blur: float) = +proc setFontBlur*(context: Context, blur: float) = context.fontBlur(blur) -proc setTextLetterSpacing*(spacing: float) = +proc setTextLetterSpacing*(context: Context, spacing: float) = context.textLetterSpacing(spacing) -proc setTextLineHeight*(lineHeight: float) = +proc setTextLineHeight*(context: Context, lineHeight: float) = context.textLineHeight(lineHeight) proc `or`(align1: Alignment, align2: Alignment): Alignment = return (align1.cint or align2.cint).Alignment -proc textAlign*(alignments: varargs[Alignment]) = +proc textAlign*(context: Context, alignments: varargs[Alignment]) = var alignment = reduce(alignments, `or`) context.textAlign(alignment.cint) -macro textAlign*(descriptor: string) = - result = createExpr("Alignment", "textAlign", descriptor) +macro textAlign*(context: Context, descriptor: string) = + result = createExpr(context, "Alignment", "textAlign", descriptor) -proc textBounds*(text: string, x, y: float): TextBounds = +proc textBounds*(context: Context, text: string, x, y: float): 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*(text: string, x, y: float) = +proc text*(context: Context, text: string, x, y: float) = discard context.text(x, y, text, nil) -proc textBoxBounds*(text: string, x, y, breakRowWidth: float): Bounds = +proc textBoxBounds*(context: Context, text: string, x, y, breakRowWidth: float): 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*(text: string, x, y, breakRowWidth: float) = +proc textBox*(context: Context, text: string, x, y, breakRowWidth: float) = context.textBox(x, y, breakRowWidth, text, nil) -proc textMetrics*(): TextMetrics = +proc textMetrics*(context: Context): TextMetrics = var ascender, descender, lineHeight: cfloat @@ -239,7 +237,7 @@ proc textMetrics*(): TextMetrics = return (ascender.float, descender.float, lineHeight.float) -iterator glyphPositions(start: cstring, endPos: cstring, x, y: float): GlyphPosition = +iterator glyphPositions(context: Context, start: cstring, endPos: cstring, x, y: float): GlyphPosition = const maxPositions = 100 var positions: array[maxPositions, NVGglyphPosition] @@ -247,7 +245,7 @@ iterator glyphPositions(start: cstring, endPos: cstring, x, y: float): GlyphPosi bytesProcessed = 0 lastX = x strIndex = 0 - lineHeight = textMetrics().lineHeight + lineHeight = context.textMetrics().lineHeight let strLen = cast[ByteAddress](endPos) - cast[ByteAddress](start) @@ -285,17 +283,17 @@ iterator glyphPositions(start: cstring, endPos: cstring, x, y: float): GlyphPosi lastX = positions[numGlyphs-1].x strIndex += maxPositions -proc glyphPositions*(text: string, x, y: float): seq[GlyphPosition] = +proc glyphPositions*(context: Context, text: string, x, y: float): seq[GlyphPosition] = result = newSeqOfCap[GlyphPosition](100) - for gpos in glyphPositions(cast[cstring](text[0].unsafeAddr), nil, x, y): + for gpos in context.glyphPositions(cast[cstring](text[0].unsafeAddr), nil, x, y): result.add gpos -proc glyphPositions*(row: NVGtextRow, x, y: float): seq[GlyphPosition] = +proc glyphPositions*(context: Context, row: NVGtextRow, x, y: float): seq[GlyphPosition] = result = newSeqOfCap[GlyphPosition](100) - for gpos in glyphPositions(row.start, row.`end`, x, y): + for gpos in context.glyphPositions(row.start, row.`end`, x, y): result.add gpos -iterator textRows(text: string, x, y, maxWidth: float): NVGtextRow = +iterator textRows(context: Context, text: string, x, y, maxWidth: float): NVGtextRow = const maxRows = 10 var rows: array[maxRows, NVGtextRow] @@ -315,48 +313,43 @@ iterator textRows(text: string, x, y, maxWidth: float): NVGtextRow = start = rows[numRows-1].next -proc glyphPositions*(text: string, x, y, maxWidth: float): seq[GlyphPosition] = +proc glyphPositions*(context: Context, text: string, x, y, maxWidth: float): seq[GlyphPosition] = var newY = y - for row in textRows(text, x, y, maxWidth): - let glyphPositions = row.glyphPositions(x, newY) + for row in context.textRows(text, x, y, maxWidth): + let glyphPositions = context.glyphPositions(row, x, newY) result.add(glyphPositions) newY += glyphPositions[0].height -proc text*(text: string, x, y, maxWidth: float) = +proc text*(context: Context, text: string, x, y, maxWidth: float) = var newY = y - for row in textRows(text, x, y, maxWidth): + for row in context.textRows(text, x, y, maxWidth): discard context.text(x, newY, row.start, row.`end`) - newY += textMetrics().lineHeight + newY += context.textMetrics().lineHeight -proc beginFrame*(width, height, pxRatio: float) = - context.beginFrame(width, height, pxRatio) +export beginFrame +export endFrame +export cancelFrame -proc cancelFrame*() = - context.cancelFrame() - -proc endFrame*() = - context.endFrame() - -proc globalCompositeOperation*(op: CompositeOperation) = +proc globalCompositeOperation*(context: Context, op: CompositeOperation) = context.globalCompositeOperation(op.cint) -proc globalCompositeBlendFunc*(sourceFactor, destFactor: BlendFactor) = +proc globalCompositeBlendFunc*(context: Context, sourceFactor, destFactor: BlendFactor) = context.globalCompositeBlendFunc(sourceFactor.cint, destFactor.cint) -proc globalCompositeBlendFuncSeparate*(srcRGB, destRGB, srcAlpha, destAlpha: BlendFactor) = +proc globalCompositeBlendFuncSeparate*(context: Context, srcRGB, destRGB, srcAlpha, destAlpha: BlendFactor) = context.globalCompositeBlendFuncSeparate(srcRGB.cint, destRGB.cint, srcAlpha.cint, destAlpha.cint) -template withFrame*(window: View, code: untyped) = +template withFrame*(context: Context, 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) + context.beginFrame(winSize.w.cfloat, winSize.h.cfloat, pxRatio) code - endFrame() + context.endFrame() # @@ -366,98 +359,140 @@ template withFrame*(window: View, code: untyped) = # The state contains transform, fill and stroke styles, text and font styles, # and scissor clipping. # -proc saveState*() = +proc saveState*(context: Context, ) = ## 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*() = +proc restoreState*(context: Context, ) = ## Pops and restores current render state. context.restore() -proc resetState*() = +proc resetState*(context: Context, ) = ## Resets current render state to default values. Does not affect the render state stack. context.reset() -template withState*(code: untyped) = - saveState() +template withState*(context: Context, code: untyped) = + context.saveState() try: code except: raise finally: - restoreState() + context.restoreState() -proc setShapeAntialias*(enabled = true) = +proc setShapeAntialias*(context: Context, enabled = true) = context.shapeAntiAlias(enabled.cint) -proc setStrokeColor*(color: Color) = +proc setStrokeColor*(context: Context, color: Color) = context.strokeColor(color) -proc setStrokePaint*(paint: Paint) = +proc setStrokePaint*(context: Context, paint: Paint) = context.strokePaint(paint) -proc setFillPaint*(paint: Paint) = +proc setFillPaint*(context: Context, paint: Paint) = context.fillPaint(paint) -proc setFillColor*(color: Color) = +proc setFillColor*(context: Context, color: Color) = context.fillColor(color) -proc setFillColor*(r, g, b: uint8) = +proc setFillColor*(context: Context, r, g, b: uint8) = context.fillColor(rgb(r.cuchar, g.cuchar, b.cuchar)) -proc setFillColor*(r, g, b: float) = +proc setFillColor*(context: Context, r, g, b: float) = context.fillColor(rgbf(r, g, b)) -proc setFillColor*(r, g, b, a: float) = +proc setFillColor*(context: Context, r, g, b, a: float) = context.fillColor(rgbaf(r, g, b, a)) -proc setFillColor*(r, g, b, a: uint8) = +proc setFillColor*(context: Context, r, g, b, a: uint8) = context.fillColor(rgba(r.cuchar, g.cuchar, b.cuchar, a.cuchar)) -proc setMiterLimit*(limit: float) = +proc setMiterLimit*(context: Context, limit: float) = context.miterLimit(limit) -proc setStrokeWidth*(size: float) = +proc setStrokeWidth*(context: Context, size: float) = context.strokeWidth(size) -proc setLineCap*(cap: LineCap) = +proc setLineCap*(context: Context, cap: LineCap) = context.lineCap(cap.cint) -proc setLineJoin*(join: LineJoin) = +proc setLineJoin*(context: Context, join: LineJoin) = context.lineJoin(join.cint) -proc setGlobalAlpha*(alpha: float) = +proc setGlobalAlpha*(context: Context, alpha: float) = + context.globalAlpha(alpha) + +proc `shapeAntialias=`*(context: Context, enabled: bool) = + context.shapeAntiAlias(enabled.cint) + +proc `strokeColor`*(context: Context, color: Color) = + context.strokeColor(color) + +proc `strokePaint=`*(context: Context, paint: Paint) = + context.strokePaint(paint) + +proc `fillPaint=`*(context: Context, paint: Paint) = + context.fillPaint(paint) + +proc `fillColor=`*(context: Context, color: Color) = + context.fillColor(color) + +proc `fillColor=`*(context: Context, color: tuple[r, g, b: uint8]) = + context.fillColor(rgb(color.r.cuchar, color.g.cuchar, color.b.cuchar)) + +proc `fillColor=`*(context: Context, color: tuple[r, g, b: float]) = + context.fillColor(rgbf(color.r, color.g, color.b)) + +proc `fillColor=`*(context: Context, color: tuple[r, g, b, a: float]) = + context.fillColor(rgbaf(color.r, color.g, color.b, color.a)) + +proc setFillColor*(context: Context, color: tuple[r, g, b, a: uint8]) = + context.fillColor(rgba(color.r.cuchar, color.g.cuchar, color.b.cuchar, color.a.cuchar)) + +proc `miterLimit=`*(context: Context, limit: float) = + context.miterLimit(limit) + +proc `strokeWidth=`*(context: Context, size: float) = + context.strokeWidth(size) + +proc `lineCap=`*(context: Context, cap: LineCap) = + context.lineCap(cap.cint) + +proc `lineJoin=`*(context: Context, join: LineJoin) = + context.lineJoin(join.cint) + +proc `globalAlpha=`*(context: Context, alpha: float) = context.globalAlpha(alpha) # Transforms -proc resetTransform*() = - context.resetTransform() +proc resetTransform*(context: Context) {.inline.} = + wrapper.resetTransform(context) -proc transform*(a, b, c, d, e, f: float) = - context.transform(a, b, c, d, e, f) +proc transform*(context: Context, a, b, c, d, e, f: float) = + wrapper.transform(context, a, b, c, d, e, f) -proc translate*(x, y: float) = - context.translate(x, y) +proc translate*(context: Context, x, y: float) {.inline.} = + wrapper.translate(context, x, y) -proc rotate*(angle: float) = - context.rotate(angle) +proc rotate*(context: Context, angle: float) {.inline.} = + wrapper.rotate(context, angle) -proc skewX*(angle: float) = - context.skewX(angle) +proc skewX*(context: Context, angle: float) {.inline.} = + wrapper.skewX(context, angle) -proc skewY*(angle: float) = - context.skewY(angle) +proc skewY*(context: Context, angle: float) {.inline.} = + wrapper.skewY(context, angle) -proc scale*(x, y: float) = - context.scale(x, y) +proc scale*(context: Context, x, y: float) {.inline.} = + wrapper.scale(context, x, y) proc copyArray[T, U](src: T): U = for i in 0..