func (c *glContext) fill(call *glCall) { pathSentinel := call.pathOffset + call.pathCount // Draw shapes gl.Enable(gl.STENCIL_TEST) c.setStencilMask(0xff) c.setStencilFunc(gl.ALWAYS, 0x00, 0xff) gl.ColorMask(false, false, false, false) // set bindpoint for solid loc c.setUniforms(call.uniformOffset, 0) checkError(c, "fill simple") gl.StencilOpSeparate(gl.FRONT, gl.KEEP, gl.KEEP, gl.INCR_WRAP) gl.StencilOpSeparate(gl.BACK, gl.KEEP, gl.KEEP, gl.DECR_WRAP) gl.Disable(gl.CULL_FACE) for i := call.pathOffset; i < pathSentinel; i++ { path := &c.paths[i] gl.DrawArrays(gl.TRIANGLE_FAN, path.fillOffset, path.fillCount) } gl.Enable(gl.CULL_FACE) // Draw anti-aliased pixels gl.ColorMask(true, true, true, true) c.setUniforms(call.uniformOffset+1, call.image) if c.flags&AntiAlias != 0 { c.setStencilFunc(gl.EQUAL, 0x00, 0xff) gl.StencilOp(gl.KEEP, gl.KEEP, gl.KEEP) // Draw fringes for i := call.pathOffset; i < pathSentinel; i++ { path := &c.paths[i] gl.DrawArrays(gl.TRIANGLE_STRIP, path.strokeOffset, path.strokeCount) } } // Draw fill c.setStencilFunc(gl.NOTEQUAL, 0x00, 0xff) gl.StencilOp(gl.ZERO, gl.ZERO, gl.ZERO) gl.DrawArrays(gl.TRIANGLES, call.triangleOffset, call.triangleCount) gl.Disable(gl.STENCIL_TEST) }
func (c *glContext) stroke(call *glCall) { paths := c.paths[call.pathOffset : call.pathOffset+call.pathCount] if c.flags&StencilStrokes != 0 { gl.Enable(gl.STENCIL_TEST) c.setStencilMask(0xff) // Fill the stroke base without overlap c.setStencilFunc(gl.EQUAL, 0x00, 0xff) gl.StencilOp(gl.KEEP, gl.KEEP, gl.INCR) c.setUniforms(call.uniformOffset+1, call.image) checkError(c, "stroke fill 0") for i := range paths { path := &paths[i] gl.DrawArrays(gl.TRIANGLE_STRIP, path.strokeOffset, path.strokeCount) } // Draw anti-aliased pixels. c.setUniforms(call.uniformOffset, call.image) c.setStencilFunc(gl.EQUAL, 0x00, 0xff) gl.StencilOp(gl.KEEP, gl.KEEP, gl.KEEP) for i := range paths { path := &paths[i] gl.DrawArrays(gl.TRIANGLE_STRIP, path.strokeOffset, path.strokeCount) } // Clear stencil buffer. gl.ColorMask(false, false, false, false) c.setStencilFunc(gl.ALWAYS, 0x00, 0xff) gl.StencilOp(gl.ZERO, gl.ZERO, gl.ZERO) checkError(c, "stroke fill 1") for i := range paths { path := &paths[i] gl.DrawArrays(gl.TRIANGLE_STRIP, path.strokeOffset, path.strokeCount) } gl.ColorMask(true, true, true, true) gl.Disable(gl.STENCIL_TEST) } else { c.setUniforms(call.uniformOffset, call.image) checkError(c, "stroke fill") for i := range paths { path := &paths[i] gl.DrawArrays(gl.TRIANGLE_STRIP, path.strokeOffset, path.strokeCount) } } }
func (b *blitter) commitGlyphs(ctx *context) { tc := b.glyphBatch.GlyphPage if tc == nil { return } sw, sh := tc.sizePixels.WH() dw, dh := ctx.sizePixels.WH() mSrc := math.CreateMat3( 1.0/float32(sw), 0, 0, 0, 1.0/float32(sh), 0, 0.0, 0.0, 1, ) mDst := math.CreateMat3( +2.0/float32(dw), 0, 0, 0, -2.0/float32(dh), 0, -1.0, +1.0, 1, ) vb := newVertexBuffer( newVertexStream("aDst", stFloatVec2, b.glyphBatch.DstRects), newVertexStream("aSrc", stFloatVec2, b.glyphBatch.SrcRects), newVertexStream("aClp", stFloatVec4, b.glyphBatch.ClipRects), newVertexStream("aCol", stFloatVec4, b.glyphBatch.Colors), ) ib := newIndexBuffer(ptUshort, b.glyphBatch.Indices) s := newShape(vb, ib, dmTriangles) gl.Disable(gl.SCISSOR_TEST) s.draw(ctx, b.fontShader, uniformBindings{ "source": tc, "mDst": mDst, "mSrc": mSrc, }) gl.Enable(gl.SCISSOR_TEST) s.release() b.glyphBatch.GlyphPage = nil b.glyphBatch.DstRects = b.glyphBatch.DstRects[:0] b.glyphBatch.SrcRects = b.glyphBatch.SrcRects[:0] b.glyphBatch.ClipRects = b.glyphBatch.ClipRects[:0] b.glyphBatch.Colors = b.glyphBatch.Colors[:0] b.glyphBatch.Indices = b.glyphBatch.Indices[:0] b.stats.drawCallCount++ }
func newViewport(driver *driver, width, height int, title string, fullscreen bool) *viewport { v := &viewport{ fullscreen: fullscreen, scaling: 1, title: title, } glfw.DefaultWindowHints() glfw.WindowHint(glfw.Samples, 4) var monitor *glfw.Monitor if fullscreen { monitor = glfw.GetPrimaryMonitor() if width == 0 || height == 0 { vm := monitor.GetVideoMode() width, height = vm.Width, vm.Height } } wnd, err := glfw.CreateWindow(width, height, v.title, monitor, nil) if err != nil { panic(err) } width, height = wnd.GetSize() // At this time, width and height serve as a "hint" for glfw.CreateWindow, so get actual values from window. wnd.MakeContextCurrent() v.context = newContext() cursorPoint := func(x, y float64) math.Point { // HACK: xpos is off by 1 and ypos is off by 3 on OSX. // Compensate until real fix is found. x -= 1.0 y -= 3.0 return math.Point{X: int(x), Y: int(y)}.ScaleS(1 / v.scaling) } wnd.SetCloseCallback(func(*glfw.Window) { v.Close() }) wnd.SetPosCallback(func(w *glfw.Window, x, y int) { v.Lock() v.position = math.NewPoint(x, y) v.Unlock() }) wnd.SetSizeCallback(func(_ *glfw.Window, w, h int) { v.Lock() v.sizeDipsUnscaled = math.Size{W: w, H: h} v.sizeDips = v.sizeDipsUnscaled.ScaleS(1 / v.scaling) v.Unlock() v.onResize.Fire() }) wnd.SetFramebufferSizeCallback(func(_ *glfw.Window, w, h int) { v.Lock() v.sizePixels = math.Size{W: w, H: h} v.Unlock() gl.Viewport(0, 0, w, h) gl.ClearColor(kClearColorR, kClearColorG, kClearColorB, 1.0) gl.Clear(gl.COLOR_BUFFER_BIT) }) wnd.SetCursorPosCallback(func(w *glfw.Window, x, y float64) { p := cursorPoint(w.GetCursorPos()) v.Lock() if v.pendingMouseMoveEvent == nil { v.pendingMouseMoveEvent = &gxui.MouseEvent{} driver.Call(func() { v.Lock() ev := *v.pendingMouseMoveEvent v.pendingMouseMoveEvent = nil v.Unlock() v.onMouseMove.Fire(ev) }) } v.pendingMouseMoveEvent.Point = p v.pendingMouseMoveEvent.State = getMouseState(w) v.Unlock() }) wnd.SetCursorEnterCallback(func(w *glfw.Window, entered bool) { p := cursorPoint(w.GetCursorPos()) ev := gxui.MouseEvent{ Point: p, } ev.State = getMouseState(w) if entered { v.onMouseEnter.Fire(ev) } else { v.onMouseExit.Fire(ev) } }) wnd.SetScrollCallback(func(w *glfw.Window, xoff, yoff float64) { p := cursorPoint(w.GetCursorPos()) v.Lock() if v.pendingMouseScrollEvent == nil { v.pendingMouseScrollEvent = &gxui.MouseEvent{} driver.Call(func() { v.Lock() ev := *v.pendingMouseScrollEvent v.pendingMouseScrollEvent = nil ev.ScrollX, ev.ScrollY = int(v.scrollAccumX), int(v.scrollAccumY) if ev.ScrollX != 0 || ev.ScrollY != 0 { v.scrollAccumX -= float64(ev.ScrollX) v.scrollAccumY -= float64(ev.ScrollY) v.Unlock() v.onMouseScroll.Fire(ev) } else { v.Unlock() } }) } v.pendingMouseScrollEvent.Point = p v.scrollAccumX += xoff * platform.ScrollSpeed v.scrollAccumY += yoff * platform.ScrollSpeed v.pendingMouseScrollEvent.State = getMouseState(w) v.Unlock() }) wnd.SetMouseButtonCallback(func(w *glfw.Window, button glfw.MouseButton, action glfw.Action, mod glfw.ModifierKey) { p := cursorPoint(w.GetCursorPos()) ev := gxui.MouseEvent{ Point: p, Modifier: translateKeyboardModifier(mod), } ev.Button = translateMouseButton(button) ev.State = getMouseState(w) if action == glfw.Press { v.onMouseDown.Fire(ev) } else { v.onMouseUp.Fire(ev) } }) wnd.SetKeyCallback(func(w *glfw.Window, key glfw.Key, scancode int, action glfw.Action, mods glfw.ModifierKey) { ev := gxui.KeyboardEvent{ Key: translateKeyboardKey(key), Modifier: translateKeyboardModifier(mods), } switch action { case glfw.Press: v.onKeyDown.Fire(ev) case glfw.Release: v.onKeyUp.Fire(ev) case glfw.Repeat: v.onKeyRepeat.Fire(ev) } }) wnd.SetCharModsCallback(func(w *glfw.Window, char rune, mods glfw.ModifierKey) { if !unicode.IsControl(char) && !unicode.IsGraphic(char) && !unicode.IsLetter(char) && !unicode.IsMark(char) && !unicode.IsNumber(char) && !unicode.IsPunct(char) && !unicode.IsSpace(char) && !unicode.IsSymbol(char) { return // Weird unicode character. Ignore } ev := gxui.KeyStrokeEvent{ Character: char, Modifier: translateKeyboardModifier(mods), } v.onKeyStroke.Fire(ev) }) wnd.SetRefreshCallback(func(w *glfw.Window) { if v.canvas != nil { v.render() } }) fw, fh := wnd.GetFramebufferSize() posX, posY := wnd.GetPos() // Pre-multiplied alpha blending gl.BlendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA) gl.Enable(gl.BLEND) gl.Enable(gl.SCISSOR_TEST) gl.Viewport(0, 0, fw, fh) gl.Scissor(0, 0, int32(fw), int32(fh)) gl.ClearColor(kClearColorR, kClearColorG, kClearColorB, 1.0) gl.Clear(gl.COLOR_BUFFER_BIT) wnd.SwapBuffers() v.window = wnd v.driver = driver v.onClose = driver.createAppEvent(func() {}) v.onResize = driver.createAppEvent(func() {}) v.onMouseMove = gxui.CreateEvent(func(gxui.MouseEvent) {}) v.onMouseEnter = driver.createAppEvent(func(gxui.MouseEvent) {}) v.onMouseExit = driver.createAppEvent(func(gxui.MouseEvent) {}) v.onMouseDown = driver.createAppEvent(func(gxui.MouseEvent) {}) v.onMouseUp = driver.createAppEvent(func(gxui.MouseEvent) {}) v.onMouseScroll = gxui.CreateEvent(func(gxui.MouseEvent) {}) v.onKeyDown = driver.createAppEvent(func(gxui.KeyboardEvent) {}) v.onKeyUp = driver.createAppEvent(func(gxui.KeyboardEvent) {}) v.onKeyRepeat = driver.createAppEvent(func(gxui.KeyboardEvent) {}) v.onKeyStroke = driver.createAppEvent(func(gxui.KeyStrokeEvent) {}) v.onDestroy = driver.createDriverEvent(func() {}) v.sizeDipsUnscaled = math.Size{W: width, H: height} v.sizeDips = v.sizeDipsUnscaled.ScaleS(1 / v.scaling) v.sizePixels = math.Size{W: fw, H: fh} v.position = math.Point{X: posX, Y: posY} return v }
func main() { err := glfw.Init(gl.ContextWatcher) if err != nil { panic(err) } defer glfw.Terminate() glfw.WindowHint(glfw.StencilBits, 1) glfw.WindowHint(glfw.Samples, 4) window, err := glfw.CreateWindow(1000*0.6, 600*0.6, "NanoVGo", nil, nil) if err != nil { panic(err) } window.SetKeyCallback(key) window.MakeContextCurrent() ctx, err := nanovgo.NewContext(0) defer ctx.Delete() if err != nil { panic(err) } demoData := LoadDemo(ctx) glfw.SwapInterval(0) fps := perfgraph.NewPerfGraph("Frame Time", "sans") for !window.ShouldClose() { t, _ := fps.UpdateGraph() fbWidth, fbHeight := window.GetFramebufferSize() winWidth, winHeight := window.GetSize() mx, my := window.GetCursorPos() pixelRatio := float32(fbWidth) / float32(winWidth) gl.Viewport(0, 0, fbWidth, fbHeight) if premult { gl.ClearColor(0, 0, 0, 0) } else { gl.ClearColor(0.3, 0.3, 0.32, 1.0) } gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT) gl.Enable(gl.BLEND) gl.BlendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA) gl.Enable(gl.CULL_FACE) gl.Disable(gl.DEPTH_TEST) ctx.BeginFrame(winWidth, winHeight, pixelRatio) demo.RenderDemo(ctx, float32(mx), float32(my), float32(winWidth), float32(winHeight), t, blowup, demoData) fps.RenderGraph(ctx, 5, 5) ctx.EndFrame() gl.Enable(gl.DEPTH_TEST) window.SwapBuffers() glfw.PollEvents() } demoData.FreeData(ctx) }
func (p *glParams) renderFlush() { c := p.context if len(c.calls) > 0 { gl.UseProgram(c.shader.program) gl.BlendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA) gl.Enable(gl.CULL_FACE) gl.CullFace(gl.BACK) gl.FrontFace(gl.CCW) gl.Enable(gl.BLEND) gl.Disable(gl.DEPTH_TEST) gl.Disable(gl.SCISSOR_TEST) gl.ColorMask(true, true, true, true) gl.StencilMask(0xffffffff) gl.StencilOp(gl.KEEP, gl.KEEP, gl.KEEP) gl.StencilFunc(gl.ALWAYS, 0, 0xffffffff) gl.ActiveTexture(gl.TEXTURE0) gl.BindTexture(gl.TEXTURE_2D, gl.Texture{}) c.stencilMask = 0xffffffff c.stencilFunc = gl.ALWAYS c.stencilFuncRef = 0 c.stencilFuncMask = 0xffffffff b := castFloat32ToByte(c.vertexes) //dumpLog("vertex:", c.vertexes) // Upload vertex data gl.BindBuffer(gl.ARRAY_BUFFER, c.vertexBuffer) gl.BufferData(gl.ARRAY_BUFFER, b, gl.STREAM_DRAW) gl.EnableVertexAttribArray(c.shader.vertexAttrib) gl.EnableVertexAttribArray(c.shader.tcoordAttrib) gl.VertexAttribPointer(c.shader.vertexAttrib, 2, gl.FLOAT, false, 4*4, 0) gl.VertexAttribPointer(c.shader.tcoordAttrib, 2, gl.FLOAT, false, 4*4, 8) // Set view and texture just once per frame. gl.Uniform1i(c.shader.locations[glnvgLocTEX], 0) gl.Uniform2fv(c.shader.locations[glnvgLocVIEWSIZE], c.view[:]) for i := range c.calls { call := &c.calls[i] switch call.callType { case glnvgFILL: c.fill(call) case glnvgCONVEXFILL: c.convexFill(call) case glnvgSTROKE: c.stroke(call) case glnvgTRIANGLES: c.triangles(call) } } gl.DisableVertexAttribArray(c.shader.vertexAttrib) gl.DisableVertexAttribArray(c.shader.tcoordAttrib) gl.Disable(gl.CULL_FACE) gl.BindBuffer(gl.ARRAY_BUFFER, gl.Buffer{}) gl.UseProgram(gl.Program{}) c.bindTexture(nil) } c.vertexes = c.vertexes[:0] c.paths = c.paths[:0] c.calls = c.calls[:0] c.uniforms = c.uniforms[:0] }