diff --git a/examples/shiny/shiny.go b/examples/shiny/shiny.go new file mode 100644 index 0000000..e0def8b --- /dev/null +++ b/examples/shiny/shiny.go @@ -0,0 +1,51 @@ +package main + +import ( + "log" + + "github.com/tfriedel6/canvas" + "github.com/tfriedel6/canvas/glimpl/xmobile" + "golang.org/x/exp/shiny/driver/gldriver" + "golang.org/x/exp/shiny/screen" + "golang.org/x/exp/shiny/widget" + "golang.org/x/exp/shiny/widget/glwidget" + "golang.org/x/exp/shiny/widget/node" +) + +var cv *canvas.Canvas +var sheet *widget.Sheet + +func main() { + gldriver.Main(func(s screen.Screen) { + glw := glwidget.NewGL(draw) + sheet = widget.NewSheet(glw) + canvas.LoadGL(glimplxmobile.New(glw.Ctx)) + cv = canvas.New(0, 0, 600, 600) + + err := widget.RunWindow(s, sheet, &widget.RunWindowOptions{ + NewWindowOptions: screen.NewWindowOptions{ + Title: "Shiny Canvas Example", + Width: 600, + Height: 600, + }, + }) + if err != nil { + log.Fatal(err) + } + }) +} + +func draw(w *glwidget.GL) { + cv.Save() + defer cv.Restore() + + cv.Translate(0, 600) + cv.Scale(1, -1) + + cv.ClearRect(0, 0, 600, 600) + cv.SetFillStyle("#FF00FF") + cv.FillRect(100, 100, 200, 200) + + w.Publish() + w.Mark(node.MarkNeedsPaintBase) +} diff --git a/glimpl/xmobile/xmobile.go b/glimpl/xmobile/xmobile.go new file mode 100644 index 0000000..0b5b770 --- /dev/null +++ b/glimpl/xmobile/xmobile.go @@ -0,0 +1,243 @@ +package glimplxmobile + +import ( + "fmt" + "reflect" + "unsafe" + + "github.com/tfriedel6/canvas" + "golang.org/x/mobile/gl" +) + +type GLImpl struct { + gl gl.Context + programs map[uint32]gl.Program +} + +var _ canvas.GL = GLImpl{} + +func New(ctx gl.Context) *GLImpl { + return &GLImpl{ + gl: ctx, + programs: make(map[uint32]gl.Program), + } +} + +func (gli GLImpl) Ptr(data interface{}) unsafe.Pointer { + if data == nil { + return unsafe.Pointer(nil) + } + var addr unsafe.Pointer + v := reflect.ValueOf(data) + switch v.Type().Kind() { + case reflect.Ptr: + e := v.Elem() + switch e.Kind() { + case + reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, + reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, + reflect.Float32, reflect.Float64: + addr = unsafe.Pointer(e.UnsafeAddr()) + default: + panic(fmt.Errorf("unsupported pointer to type %s; must be a slice or pointer to a singular scalar value or the first element of an array or slice", e.Kind())) + } + case reflect.Uintptr: + addr = unsafe.Pointer(v.Pointer()) + case reflect.Slice: + addr = unsafe.Pointer(v.Index(0).UnsafeAddr()) + default: + panic(fmt.Errorf("unsupported type %s; must be a slice or pointer to a singular scalar value or the first element of an array or slice", v.Type())) + } + return addr +} +func (gli GLImpl) ActiveTexture(texture uint32) { + gli.gl.ActiveTexture(gl.Enum(texture)) +} +func (gli GLImpl) AttachShader(program uint32, shader uint32) { + gli.gl.AttachShader(gli.programs[program], gl.Shader{Value: shader}) +} +func (gli GLImpl) BindBuffer(target uint32, buffer uint32) { + gli.gl.BindBuffer(gl.Enum(target), gl.Buffer{Value: buffer}) +} +func (gli GLImpl) BindTexture(target uint32, texture uint32) { + gli.gl.BindTexture(gl.Enum(target), gl.Texture{Value: texture}) +} +func (gli GLImpl) BlendFunc(sfactor uint32, dfactor uint32) { + gli.gl.BlendFunc(gl.Enum(sfactor), gl.Enum(dfactor)) +} +func (gli GLImpl) BufferData(target uint32, size int, data unsafe.Pointer, usage uint32) { + var buf []byte + sh := (*reflect.SliceHeader)(unsafe.Pointer(&buf)) + sh.Cap = size + sh.Len = size + sh.Data = uintptr(data) + gli.gl.BufferData(gl.Enum(target), buf, gl.Enum(usage)) +} +func (gli GLImpl) Clear(mask uint32) { + gli.gl.Clear(gl.Enum(mask)) +} +func (gli GLImpl) ClearColor(red float32, green float32, blue float32, alpha float32) { + gli.gl.ClearColor(red, green, blue, alpha) +} +func (gli GLImpl) ColorMask(red bool, green bool, blue bool, alpha bool) { + gli.gl.ColorMask(red, green, blue, alpha) +} +func (gli GLImpl) CompileShader(shader uint32) { + gli.gl.CompileShader(gl.Shader{Value: shader}) +} +func (gli GLImpl) CreateProgram() uint32 { + program := gli.gl.CreateProgram() + gli.programs[program.Value] = program + return program.Value +} +func (gli GLImpl) CreateShader(xtype uint32) uint32 { + return gli.gl.CreateShader(gl.Enum(xtype)).Value +} +func (gli GLImpl) DeleteShader(shader uint32) { + gli.gl.DeleteShader(gl.Shader{Value: shader}) +} +func (gli GLImpl) DeleteTextures(n int32, textures *uint32) { + var buf []uint32 + sh := (*reflect.SliceHeader)(unsafe.Pointer(&buf)) + sh.Cap = int(n) + sh.Len = int(n) + sh.Data = uintptr(unsafe.Pointer(textures)) + for i := 0; i < int(n); i++ { + gli.gl.DeleteTexture(gl.Texture{Value: buf[i]}) + } +} +func (gli GLImpl) Disable(cap uint32) { + gli.gl.Disable(gl.Enum(cap)) +} +func (gli GLImpl) DisableVertexAttribArray(index uint32) { + gli.gl.DisableVertexAttribArray(gl.Attrib{Value: uint(index)}) +} +func (gli GLImpl) DrawArrays(mode uint32, first int32, count int32) { + gli.gl.DrawArrays(gl.Enum(mode), int(first), int(count)) +} +func (gli GLImpl) Enable(cap uint32) { + gli.gl.Enable(gl.Enum(cap)) +} +func (gli GLImpl) EnableVertexAttribArray(index uint32) { + gli.gl.EnableVertexAttribArray(gl.Attrib{Value: uint(index)}) +} +func (gli GLImpl) GenBuffers(n int32, buffers *uint32) { + var buf []uint32 + sh := (*reflect.SliceHeader)(unsafe.Pointer(&buf)) + sh.Cap = int(n) + sh.Len = int(n) + sh.Data = uintptr(unsafe.Pointer(buffers)) + for i := 0; i < int(n); i++ { + buf[i] = gli.gl.CreateBuffer().Value + } +} +func (gli GLImpl) GenTextures(n int32, textures *uint32) { + var buf []uint32 + sh := (*reflect.SliceHeader)(unsafe.Pointer(&buf)) + sh.Cap = int(n) + sh.Len = int(n) + sh.Data = uintptr(unsafe.Pointer(textures)) + for i := 0; i < int(n); i++ { + buf[i] = gli.gl.CreateTexture().Value + } +} +func (gli GLImpl) GenerateMipmap(target uint32) { + gli.gl.GenerateMipmap(gl.Enum(target)) +} +func (gli GLImpl) GetAttribLocation(program uint32, name string) int32 { + return int32(gli.gl.GetAttribLocation(gli.programs[program], name).Value) +} +func (gli GLImpl) GetError() uint32 { + return uint32(gli.gl.GetError()) +} +func (gli GLImpl) GetProgramInfoLog(program uint32) string { + return gli.gl.GetProgramInfoLog(gli.programs[program]) +} +func (gli GLImpl) GetProgramiv(program uint32, pname uint32, params *int32) { + i := gli.gl.GetProgrami(gli.programs[program], gl.Enum(pname)) + *params = int32(i) +} +func (gli GLImpl) GetShaderInfoLog(program uint32) string { + return gli.gl.GetShaderInfoLog(gl.Shader{Value: program}) +} +func (gli GLImpl) GetShaderiv(shader uint32, pname uint32, params *int32) { + i := gli.gl.GetShaderi(gl.Shader{Value: shader}, gl.Enum(pname)) + *params = int32(i) +} +func (gli GLImpl) GetUniformLocation(program uint32, name string) int32 { + return gli.gl.GetUniformLocation(gli.programs[program], name).Value +} +func (gli GLImpl) LinkProgram(program uint32) { + gli.gl.LinkProgram(gli.programs[program]) +} +func (gli GLImpl) ReadPixels(x int32, y int32, width int32, height int32, format uint32, xtype uint32, pixels unsafe.Pointer) { + var buf []byte + sh := (*reflect.SliceHeader)(unsafe.Pointer(&buf)) + sh.Cap = int(width * height * 4) + sh.Len = int(width * height * 4) + sh.Data = uintptr(pixels) + gli.gl.ReadPixels(buf, int(x), int(y), int(width), int(height), gl.Enum(format), gl.Enum(xtype)) +} +func (gli GLImpl) Scissor(x int32, y int32, width int32, height int32) { + gli.gl.Scissor(x, y, width, height) +} +func (gli GLImpl) ShaderSource(shader uint32, source string) { + gli.gl.ShaderSource(gl.Shader{Value: shader}, source) +} +func (gli GLImpl) StencilFunc(xfunc uint32, ref int32, mask uint32) { + gli.gl.StencilFunc(gl.Enum(xfunc), int(ref), mask) +} +func (gli GLImpl) StencilMask(mask uint32) { + gli.gl.StencilMask(mask) +} +func (gli GLImpl) StencilOp(fail uint32, zfail uint32, zpass uint32) { + gli.gl.StencilOp(gl.Enum(fail), gl.Enum(zfail), gl.Enum(zpass)) +} +func (gli GLImpl) TexImage2D(target uint32, level int32, internalformat int32, width int32, height int32, border int32, format uint32, xtype uint32, pixels unsafe.Pointer) { + var buf []byte + sh := (*reflect.SliceHeader)(unsafe.Pointer(&buf)) + sh.Cap = int(width * height * 4) + sh.Len = int(width * height * 4) + sh.Data = uintptr(pixels) + gli.gl.TexImage2D(gl.Enum(target), int(level), int(width), int(height), gl.Enum(format), gl.Enum(xtype), buf) +} +func (gli GLImpl) TexParameteri(target uint32, pname uint32, param int32) { + gli.gl.TexParameteri(gl.Enum(target), gl.Enum(pname), int(param)) +} +func (gli GLImpl) TexSubImage2D(target uint32, level int32, xoffset int32, yoffset int32, width int32, height int32, format uint32, xtype uint32, pixels unsafe.Pointer) { + var buf []byte + sh := (*reflect.SliceHeader)(unsafe.Pointer(&buf)) + sh.Cap = int(width * height * 4) + sh.Len = int(width * height * 4) + sh.Data = uintptr(pixels) + gli.gl.TexSubImage2D(gl.Enum(target), int(level), int(xoffset), int(yoffset), int(width), int(height), gl.Enum(format), gl.Enum(xtype), buf) +} +func (gli GLImpl) Uniform1f(location int32, v0 float32) { + gli.gl.Uniform1f(gl.Uniform{Value: location}, v0) +} +func (gli GLImpl) Uniform1i(location int32, v0 int32) { + gli.gl.Uniform1i(gl.Uniform{Value: location}, int(v0)) +} +func (gli GLImpl) Uniform2f(location int32, v0 float32, v1 float32) { + gli.gl.Uniform2f(gl.Uniform{Value: location}, v0, v1) +} +func (gli GLImpl) Uniform4f(location int32, v0 float32, v1 float32, v2 float32, v3 float32) { + gli.gl.Uniform4f(gl.Uniform{Value: location}, v0, v1, v2, v3) +} +func (gli GLImpl) UniformMatrix3fv(location int32, count int32, transpose bool, value *float32) { + var buf []float32 + sh := (*reflect.SliceHeader)(unsafe.Pointer(&buf)) + sh.Cap = 9 + sh.Len = 9 + sh.Data = uintptr(unsafe.Pointer(value)) + gli.gl.UniformMatrix3fv(gl.Uniform{Value: location}, buf) +} +func (gli GLImpl) UseProgram(program uint32) { + gli.gl.UseProgram(gli.programs[program]) +} +func (gli GLImpl) VertexAttribPointer(index uint32, size int32, xtype uint32, normalized bool, stride int32, offset uint32) { + gli.gl.VertexAttribPointer(gl.Attrib{Value: uint(index)}, int(size), gl.Enum(xtype), normalized, int(stride), int(offset)) +} +func (gli GLImpl) Viewport(x int32, y int32, width int32, height int32) { + gli.gl.Viewport(int(x), int(y), int(width), int(height)) +}