chore(v2): vendor dependencies for offline/China builds
go mod vendor pins onnxruntime_go v1.12.1, Gio and the rest into v2/vendor so go run/build work without hitting proxy.golang.org (blocked/slow in China). Verified: CGO_ENABLED=1 go build -mod=vendor ./internal/spike and GOOS=windows go build -mod=vendor ./internal/ui both pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+36
@@ -0,0 +1,36 @@
|
||||
// SPDX-License-Identifier: Unlicense OR MIT
|
||||
|
||||
// Package byteslice provides byte slice views of other Go values such as
|
||||
// slices and structs.
|
||||
package byteslice
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Struct returns a byte slice view of a struct.
|
||||
func Struct(s any) []byte {
|
||||
v := reflect.ValueOf(s)
|
||||
sz := int(v.Elem().Type().Size())
|
||||
return unsafe.Slice((*byte)(unsafe.Pointer(v.Pointer())), sz)
|
||||
}
|
||||
|
||||
// Uint32 returns a byte slice view of a uint32 slice.
|
||||
func Uint32(s []uint32) []byte {
|
||||
n := len(s)
|
||||
if n == 0 {
|
||||
return nil
|
||||
}
|
||||
blen := n * int(unsafe.Sizeof(s[0]))
|
||||
return unsafe.Slice((*byte)(unsafe.Pointer(&s[0])), blen)
|
||||
}
|
||||
|
||||
// Slice returns a byte slice view of a slice.
|
||||
func Slice(s any) []byte {
|
||||
v := reflect.ValueOf(s)
|
||||
first := v.Index(0)
|
||||
sz := int(first.Type().Size())
|
||||
res := unsafe.Slice((*byte)(unsafe.Pointer(v.Pointer())), sz*v.Cap())
|
||||
return res[:sz*v.Len()]
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
// SPDX-License-Identifier: Unlicense OR MIT
|
||||
|
||||
// Package cocoainit initializes support for multithreaded
|
||||
// programs in Cocoa.
|
||||
package cocoainit
|
||||
|
||||
/*
|
||||
#cgo CFLAGS: -xobjective-c -fobjc-arc
|
||||
#cgo LDFLAGS: -framework Foundation
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
static inline void activate_cocoa_multithreading() {
|
||||
[[NSThread new] start];
|
||||
}
|
||||
#pragma GCC visibility push(hidden)
|
||||
*/
|
||||
import "C"
|
||||
|
||||
func init() {
|
||||
C.activate_cocoa_multithreading()
|
||||
}
|
||||
+1694
File diff suppressed because it is too large
Load Diff
+57
@@ -0,0 +1,57 @@
|
||||
// Package debug provides general debug feature management for Gio, including
|
||||
// the ability to toggle debug features using the GIODEBUG environment variable.
|
||||
package debug
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
const (
|
||||
debugVariable = "GIODEBUG"
|
||||
textSubsystem = "text"
|
||||
silentFeature = "silent"
|
||||
)
|
||||
|
||||
// Text controls whether the text subsystem has debug logging enabled.
|
||||
var Text atomic.Bool
|
||||
|
||||
var parseOnce sync.Once
|
||||
|
||||
// Parse processes the current value of GIODEBUG. If it is unset, it does nothing.
|
||||
// Otherwise it process its value, printing usage info the stderr if the value is
|
||||
// not understood. Parse will be automatically invoked when the first application
|
||||
// window is created, allowing applications to manipulate GIODEBUG programmatically
|
||||
// before it is parsed.
|
||||
func Parse() {
|
||||
parseOnce.Do(func() {
|
||||
val, ok := os.LookupEnv(debugVariable)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
print := false
|
||||
silent := false
|
||||
for part := range strings.SplitSeq(val, ",") {
|
||||
switch part {
|
||||
case textSubsystem:
|
||||
Text.Store(true)
|
||||
case silentFeature:
|
||||
silent = true
|
||||
default:
|
||||
print = true
|
||||
}
|
||||
}
|
||||
if print && !silent {
|
||||
fmt.Fprintf(os.Stderr,
|
||||
`Usage of %s:
|
||||
A comma-delimited list of debug subsystems to enable. Currently recognized systems:
|
||||
|
||||
- %s: text debug info including system font resolution
|
||||
- %s: silence this usage message even if GIODEBUG contains invalid content
|
||||
`, debugVariable, textSubsystem, silentFeature)
|
||||
}
|
||||
})
|
||||
}
|
||||
+247
@@ -0,0 +1,247 @@
|
||||
// SPDX-License-Identifier: Unlicense OR MIT
|
||||
|
||||
//go:build linux || windows || freebsd || openbsd
|
||||
// +build linux windows freebsd openbsd
|
||||
|
||||
package egl
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"gioui.org/gpu"
|
||||
)
|
||||
|
||||
type Context struct {
|
||||
disp _EGLDisplay
|
||||
eglCtx *eglContext
|
||||
eglSurf _EGLSurface
|
||||
}
|
||||
|
||||
type eglContext struct {
|
||||
config _EGLConfig
|
||||
ctx _EGLContext
|
||||
visualID int
|
||||
srgb bool
|
||||
surfaceless bool
|
||||
}
|
||||
|
||||
var (
|
||||
nilEGLDisplay _EGLDisplay
|
||||
nilEGLSurface _EGLSurface
|
||||
nilEGLContext _EGLContext
|
||||
nilEGLConfig _EGLConfig
|
||||
EGL_DEFAULT_DISPLAY NativeDisplayType
|
||||
)
|
||||
|
||||
const (
|
||||
_EGL_ALPHA_SIZE = 0x3021
|
||||
_EGL_BLUE_SIZE = 0x3022
|
||||
_EGL_CONFIG_CAVEAT = 0x3027
|
||||
_EGL_CONTEXT_CLIENT_VERSION = 0x3098
|
||||
_EGL_DEPTH_SIZE = 0x3025
|
||||
_EGL_GL_COLORSPACE_KHR = 0x309d
|
||||
_EGL_GL_COLORSPACE_SRGB_KHR = 0x3089
|
||||
_EGL_GREEN_SIZE = 0x3023
|
||||
_EGL_EXTENSIONS = 0x3055
|
||||
_EGL_NATIVE_VISUAL_ID = 0x302e
|
||||
_EGL_NONE = 0x3038
|
||||
_EGL_OPENGL_ES2_BIT = 0x4
|
||||
_EGL_RED_SIZE = 0x3024
|
||||
_EGL_RENDERABLE_TYPE = 0x3040
|
||||
_EGL_SURFACE_TYPE = 0x3033
|
||||
_EGL_WINDOW_BIT = 0x4
|
||||
)
|
||||
|
||||
func (c *Context) Release() {
|
||||
c.ReleaseSurface()
|
||||
if c.eglCtx != nil {
|
||||
eglDestroyContext(c.disp, c.eglCtx.ctx)
|
||||
c.eglCtx = nil
|
||||
}
|
||||
eglTerminate(c.disp)
|
||||
c.disp = nilEGLDisplay
|
||||
}
|
||||
|
||||
func (c *Context) Present() error {
|
||||
if !eglSwapBuffers(c.disp, c.eglSurf) {
|
||||
return fmt.Errorf("eglSwapBuffers failed (%x)", eglGetError())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewContext(disp NativeDisplayType) (*Context, error) {
|
||||
if err := loadEGL(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
eglDisp := eglGetDisplay(disp)
|
||||
// eglGetDisplay can return EGL_NO_DISPLAY yet no error
|
||||
// (EGL_SUCCESS), in which case a default EGL display might be
|
||||
// available.
|
||||
if eglDisp == nilEGLDisplay {
|
||||
eglDisp = eglGetDisplay(EGL_DEFAULT_DISPLAY)
|
||||
}
|
||||
if eglDisp == nilEGLDisplay {
|
||||
return nil, fmt.Errorf("eglGetDisplay failed: 0x%x", eglGetError())
|
||||
}
|
||||
eglCtx, err := createContext(eglDisp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c := &Context{
|
||||
disp: eglDisp,
|
||||
eglCtx: eglCtx,
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (c *Context) RenderTarget() (gpu.RenderTarget, error) {
|
||||
return gpu.OpenGLRenderTarget{}, nil
|
||||
}
|
||||
|
||||
func (c *Context) API() gpu.API {
|
||||
return gpu.OpenGL{}
|
||||
}
|
||||
|
||||
func (c *Context) ReleaseSurface() {
|
||||
if c.eglSurf == nilEGLSurface {
|
||||
return
|
||||
}
|
||||
// Make sure any in-flight GL commands are complete.
|
||||
eglWaitClient()
|
||||
c.ReleaseCurrent()
|
||||
eglDestroySurface(c.disp, c.eglSurf)
|
||||
c.eglSurf = nilEGLSurface
|
||||
}
|
||||
|
||||
func (c *Context) VisualID() int {
|
||||
return c.eglCtx.visualID
|
||||
}
|
||||
|
||||
func (c *Context) CreateSurface(win NativeWindowType) error {
|
||||
eglSurf, err := createSurface(c.disp, c.eglCtx, win)
|
||||
c.eglSurf = eglSurf
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Context) ReleaseCurrent() {
|
||||
if c.disp != nilEGLDisplay {
|
||||
eglMakeCurrent(c.disp, nilEGLSurface, nilEGLSurface, nilEGLContext)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Context) MakeCurrent() error {
|
||||
// OpenGL contexts are implicit and thread-local. Lock the OS thread.
|
||||
runtime.LockOSThread()
|
||||
|
||||
if c.eglSurf == nilEGLSurface && !c.eglCtx.surfaceless {
|
||||
return errors.New("no surface created yet EGL_KHR_surfaceless_context is not supported")
|
||||
}
|
||||
if !eglMakeCurrent(c.disp, c.eglSurf, c.eglSurf, c.eglCtx.ctx) {
|
||||
return fmt.Errorf("eglMakeCurrent error 0x%x", eglGetError())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Context) EnableVSync(enable bool) {
|
||||
if enable {
|
||||
eglSwapInterval(c.disp, 1)
|
||||
} else {
|
||||
eglSwapInterval(c.disp, 0)
|
||||
}
|
||||
}
|
||||
|
||||
func hasExtension(exts []string, ext string) bool {
|
||||
return slices.Contains(exts, ext)
|
||||
}
|
||||
|
||||
func createContext(disp _EGLDisplay) (*eglContext, error) {
|
||||
major, minor, ret := eglInitialize(disp)
|
||||
if !ret {
|
||||
return nil, fmt.Errorf("eglInitialize failed: 0x%x", eglGetError())
|
||||
}
|
||||
// sRGB framebuffer support on EGL 1.5 or if EGL_KHR_gl_colorspace is supported.
|
||||
exts := strings.Split(eglQueryString(disp, _EGL_EXTENSIONS), " ")
|
||||
srgb := major > 1 || minor >= 5 || hasExtension(exts, "EGL_KHR_gl_colorspace")
|
||||
attribs := []_EGLint{
|
||||
_EGL_RENDERABLE_TYPE, _EGL_OPENGL_ES2_BIT,
|
||||
_EGL_SURFACE_TYPE, _EGL_WINDOW_BIT,
|
||||
_EGL_BLUE_SIZE, 8,
|
||||
_EGL_GREEN_SIZE, 8,
|
||||
_EGL_RED_SIZE, 8,
|
||||
_EGL_CONFIG_CAVEAT, _EGL_NONE,
|
||||
}
|
||||
if srgb {
|
||||
if runtime.GOOS == "linux" || runtime.GOOS == "android" {
|
||||
// Some Mesa drivers crash if an sRGB framebuffer is requested without alpha.
|
||||
// https://bugs.freedesktop.org/show_bug.cgi?id=107782.
|
||||
//
|
||||
// Also, some Android devices (Samsung S9) need alpha for sRGB to work.
|
||||
attribs = append(attribs, _EGL_ALPHA_SIZE, 8)
|
||||
}
|
||||
}
|
||||
attribs = append(attribs, _EGL_NONE)
|
||||
eglCfg, ret := eglChooseConfig(disp, attribs)
|
||||
if !ret {
|
||||
return nil, fmt.Errorf("eglChooseConfig failed: 0x%x", eglGetError())
|
||||
}
|
||||
if eglCfg == nilEGLConfig {
|
||||
supportsNoCfg := hasExtension(exts, "EGL_KHR_no_config_context")
|
||||
if !supportsNoCfg {
|
||||
return nil, errors.New("eglChooseConfig returned no configs")
|
||||
}
|
||||
}
|
||||
var visID _EGLint
|
||||
if eglCfg != nilEGLConfig {
|
||||
var ok bool
|
||||
visID, ok = eglGetConfigAttrib(disp, eglCfg, _EGL_NATIVE_VISUAL_ID)
|
||||
if !ok {
|
||||
return nil, errors.New("newContext: eglGetConfigAttrib for _EGL_NATIVE_VISUAL_ID failed")
|
||||
}
|
||||
}
|
||||
ctxAttribs := []_EGLint{
|
||||
_EGL_CONTEXT_CLIENT_VERSION, 3,
|
||||
_EGL_NONE,
|
||||
}
|
||||
eglCtx := eglCreateContext(disp, eglCfg, nilEGLContext, ctxAttribs)
|
||||
if eglCtx == nilEGLContext {
|
||||
// Fall back to OpenGL ES 2 and rely on extensions.
|
||||
ctxAttribs := []_EGLint{
|
||||
_EGL_CONTEXT_CLIENT_VERSION, 2,
|
||||
_EGL_NONE,
|
||||
}
|
||||
eglCtx = eglCreateContext(disp, eglCfg, nilEGLContext, ctxAttribs)
|
||||
if eglCtx == nilEGLContext {
|
||||
return nil, fmt.Errorf("eglCreateContext failed: 0x%x", eglGetError())
|
||||
}
|
||||
}
|
||||
return &eglContext{
|
||||
config: _EGLConfig(eglCfg),
|
||||
ctx: _EGLContext(eglCtx),
|
||||
visualID: int(visID),
|
||||
srgb: srgb,
|
||||
surfaceless: hasExtension(exts, "EGL_KHR_surfaceless_context"),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func createSurface(disp _EGLDisplay, eglCtx *eglContext, win NativeWindowType) (_EGLSurface, error) {
|
||||
var surfAttribs []_EGLint
|
||||
if eglCtx.srgb {
|
||||
surfAttribs = append(surfAttribs, _EGL_GL_COLORSPACE_KHR, _EGL_GL_COLORSPACE_SRGB_KHR)
|
||||
}
|
||||
surfAttribs = append(surfAttribs, _EGL_NONE)
|
||||
eglSurf := eglCreateWindowSurface(disp, eglCtx.config, win, surfAttribs)
|
||||
if eglSurf == nilEGLSurface && eglCtx.srgb {
|
||||
// Try again without sRGB.
|
||||
eglCtx.srgb = false
|
||||
surfAttribs = []_EGLint{_EGL_NONE}
|
||||
eglSurf = eglCreateWindowSurface(disp, eglCtx.config, win, surfAttribs)
|
||||
}
|
||||
if eglSurf == nilEGLSurface {
|
||||
return nilEGLSurface, fmt.Errorf("newContext: eglCreateWindowSurface failed 0x%x (sRGB=%v)", eglGetError(), eglCtx.srgb)
|
||||
}
|
||||
return eglSurf, nil
|
||||
}
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
// SPDX-License-Identifier: Unlicense OR MIT
|
||||
|
||||
//go:build linux || freebsd || openbsd
|
||||
// +build linux freebsd openbsd
|
||||
|
||||
package egl
|
||||
|
||||
/*
|
||||
#cgo linux,!android pkg-config: egl
|
||||
#cgo freebsd openbsd android LDFLAGS: -lEGL
|
||||
#cgo freebsd CFLAGS: -I/usr/local/include
|
||||
#cgo freebsd LDFLAGS: -L/usr/local/lib
|
||||
#cgo openbsd CFLAGS: -I/usr/X11R6/include
|
||||
#cgo openbsd LDFLAGS: -L/usr/X11R6/lib
|
||||
#cgo CFLAGS: -DEGL_NO_X11
|
||||
|
||||
#include <EGL/egl.h>
|
||||
#include <EGL/eglext.h>
|
||||
*/
|
||||
import "C"
|
||||
|
||||
type (
|
||||
_EGLint = C.EGLint
|
||||
_EGLDisplay = C.EGLDisplay
|
||||
_EGLConfig = C.EGLConfig
|
||||
_EGLContext = C.EGLContext
|
||||
_EGLSurface = C.EGLSurface
|
||||
NativeDisplayType = C.EGLNativeDisplayType
|
||||
NativeWindowType = C.EGLNativeWindowType
|
||||
)
|
||||
|
||||
func loadEGL() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func eglChooseConfig(disp _EGLDisplay, attribs []_EGLint) (_EGLConfig, bool) {
|
||||
var cfg C.EGLConfig
|
||||
var ncfg C.EGLint
|
||||
if C.eglChooseConfig(disp, &attribs[0], &cfg, 1, &ncfg) != C.EGL_TRUE {
|
||||
return nilEGLConfig, false
|
||||
}
|
||||
return _EGLConfig(cfg), true
|
||||
}
|
||||
|
||||
func eglCreateContext(disp _EGLDisplay, cfg _EGLConfig, shareCtx _EGLContext, attribs []_EGLint) _EGLContext {
|
||||
ctx := C.eglCreateContext(disp, cfg, shareCtx, &attribs[0])
|
||||
return _EGLContext(ctx)
|
||||
}
|
||||
|
||||
func eglDestroySurface(disp _EGLDisplay, surf _EGLSurface) bool {
|
||||
return C.eglDestroySurface(disp, surf) == C.EGL_TRUE
|
||||
}
|
||||
|
||||
func eglDestroyContext(disp _EGLDisplay, ctx _EGLContext) bool {
|
||||
return C.eglDestroyContext(disp, ctx) == C.EGL_TRUE
|
||||
}
|
||||
|
||||
func eglGetConfigAttrib(disp _EGLDisplay, cfg _EGLConfig, attr _EGLint) (_EGLint, bool) {
|
||||
var val _EGLint
|
||||
ret := C.eglGetConfigAttrib(disp, cfg, attr, &val)
|
||||
return val, ret == C.EGL_TRUE
|
||||
}
|
||||
|
||||
func eglGetError() _EGLint {
|
||||
return C.eglGetError()
|
||||
}
|
||||
|
||||
func eglInitialize(disp _EGLDisplay) (_EGLint, _EGLint, bool) {
|
||||
var maj, min _EGLint
|
||||
ret := C.eglInitialize(disp, &maj, &min)
|
||||
return maj, min, ret == C.EGL_TRUE
|
||||
}
|
||||
|
||||
func eglMakeCurrent(disp _EGLDisplay, draw, read _EGLSurface, ctx _EGLContext) bool {
|
||||
return C.eglMakeCurrent(disp, draw, read, ctx) == C.EGL_TRUE
|
||||
}
|
||||
|
||||
func eglReleaseThread() bool {
|
||||
return C.eglReleaseThread() == C.EGL_TRUE
|
||||
}
|
||||
|
||||
func eglSwapBuffers(disp _EGLDisplay, surf _EGLSurface) bool {
|
||||
return C.eglSwapBuffers(disp, surf) == C.EGL_TRUE
|
||||
}
|
||||
|
||||
func eglSwapInterval(disp _EGLDisplay, interval _EGLint) bool {
|
||||
return C.eglSwapInterval(disp, interval) == C.EGL_TRUE
|
||||
}
|
||||
|
||||
func eglTerminate(disp _EGLDisplay) bool {
|
||||
return C.eglTerminate(disp) == C.EGL_TRUE
|
||||
}
|
||||
|
||||
func eglQueryString(disp _EGLDisplay, name _EGLint) string {
|
||||
return C.GoString(C.eglQueryString(disp, name))
|
||||
}
|
||||
|
||||
func eglGetDisplay(disp NativeDisplayType) _EGLDisplay {
|
||||
return C.eglGetDisplay(disp)
|
||||
}
|
||||
|
||||
func eglCreateWindowSurface(disp _EGLDisplay, conf _EGLConfig, win NativeWindowType, attribs []_EGLint) _EGLSurface {
|
||||
eglSurf := C.eglCreateWindowSurface(disp, conf, win, &attribs[0])
|
||||
return eglSurf
|
||||
}
|
||||
|
||||
func eglWaitClient() bool {
|
||||
return C.eglWaitClient() == C.EGL_TRUE
|
||||
}
|
||||
+187
@@ -0,0 +1,187 @@
|
||||
// SPDX-License-Identifier: Unlicense OR MIT
|
||||
|
||||
package egl
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
"sync"
|
||||
"unsafe"
|
||||
|
||||
syscall "golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
type (
|
||||
_EGLint int32
|
||||
_EGLDisplay uintptr
|
||||
_EGLConfig uintptr
|
||||
_EGLContext uintptr
|
||||
_EGLSurface uintptr
|
||||
NativeDisplayType uintptr
|
||||
NativeWindowType uintptr
|
||||
)
|
||||
|
||||
var (
|
||||
libEGL = syscall.DLL{}
|
||||
_eglChooseConfig *syscall.Proc
|
||||
_eglCreateContext *syscall.Proc
|
||||
_eglCreateWindowSurface *syscall.Proc
|
||||
_eglDestroyContext *syscall.Proc
|
||||
_eglDestroySurface *syscall.Proc
|
||||
_eglGetConfigAttrib *syscall.Proc
|
||||
_eglGetDisplay *syscall.Proc
|
||||
_eglGetError *syscall.Proc
|
||||
_eglInitialize *syscall.Proc
|
||||
_eglMakeCurrent *syscall.Proc
|
||||
_eglReleaseThread *syscall.Proc
|
||||
_eglSwapInterval *syscall.Proc
|
||||
_eglSwapBuffers *syscall.Proc
|
||||
_eglTerminate *syscall.Proc
|
||||
_eglQueryString *syscall.Proc
|
||||
_eglWaitClient *syscall.Proc
|
||||
)
|
||||
|
||||
var loadOnce = sync.OnceValue(loadDLLs)
|
||||
|
||||
func loadEGL() error {
|
||||
return loadOnce()
|
||||
}
|
||||
|
||||
func loadDLLs() error {
|
||||
if err := loadDLL(&libEGL, "libEGL.dll"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
procs := map[string]**syscall.Proc{
|
||||
"eglChooseConfig": &_eglChooseConfig,
|
||||
"eglCreateContext": &_eglCreateContext,
|
||||
"eglCreateWindowSurface": &_eglCreateWindowSurface,
|
||||
"eglDestroyContext": &_eglDestroyContext,
|
||||
"eglDestroySurface": &_eglDestroySurface,
|
||||
"eglGetConfigAttrib": &_eglGetConfigAttrib,
|
||||
"eglGetDisplay": &_eglGetDisplay,
|
||||
"eglGetError": &_eglGetError,
|
||||
"eglInitialize": &_eglInitialize,
|
||||
"eglMakeCurrent": &_eglMakeCurrent,
|
||||
"eglReleaseThread": &_eglReleaseThread,
|
||||
"eglSwapInterval": &_eglSwapInterval,
|
||||
"eglSwapBuffers": &_eglSwapBuffers,
|
||||
"eglTerminate": &_eglTerminate,
|
||||
"eglQueryString": &_eglQueryString,
|
||||
"eglWaitClient": &_eglWaitClient,
|
||||
}
|
||||
for name, proc := range procs {
|
||||
p, err := libEGL.FindProc(name)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to locate %s in %s: %w", name, libEGL.Name, err)
|
||||
}
|
||||
*proc = p
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func loadDLL(dll *syscall.DLL, name string) error {
|
||||
handle, err := syscall.LoadLibraryEx(name, 0, syscall.LOAD_LIBRARY_SEARCH_DEFAULT_DIRS)
|
||||
if err != nil {
|
||||
return fmt.Errorf("egl: failed to load %s: %v", name, err)
|
||||
}
|
||||
dll.Handle = handle
|
||||
dll.Name = name
|
||||
return nil
|
||||
}
|
||||
|
||||
func eglChooseConfig(disp _EGLDisplay, attribs []_EGLint) (_EGLConfig, bool) {
|
||||
var cfg _EGLConfig
|
||||
var ncfg _EGLint
|
||||
a := &attribs[0]
|
||||
r, _, _ := _eglChooseConfig.Call(uintptr(disp), uintptr(unsafe.Pointer(a)), uintptr(unsafe.Pointer(&cfg)), 1, uintptr(unsafe.Pointer(&ncfg)))
|
||||
issue34474KeepAlive(a)
|
||||
return cfg, r != 0
|
||||
}
|
||||
|
||||
func eglCreateContext(disp _EGLDisplay, cfg _EGLConfig, shareCtx _EGLContext, attribs []_EGLint) _EGLContext {
|
||||
a := &attribs[0]
|
||||
c, _, _ := _eglCreateContext.Call(uintptr(disp), uintptr(cfg), uintptr(shareCtx), uintptr(unsafe.Pointer(a)))
|
||||
issue34474KeepAlive(a)
|
||||
return _EGLContext(c)
|
||||
}
|
||||
|
||||
func eglCreateWindowSurface(disp _EGLDisplay, cfg _EGLConfig, win NativeWindowType, attribs []_EGLint) _EGLSurface {
|
||||
a := &attribs[0]
|
||||
s, _, _ := _eglCreateWindowSurface.Call(uintptr(disp), uintptr(cfg), uintptr(win), uintptr(unsafe.Pointer(a)))
|
||||
issue34474KeepAlive(a)
|
||||
return _EGLSurface(s)
|
||||
}
|
||||
|
||||
func eglDestroySurface(disp _EGLDisplay, surf _EGLSurface) bool {
|
||||
r, _, _ := _eglDestroySurface.Call(uintptr(disp), uintptr(surf))
|
||||
return r != 0
|
||||
}
|
||||
|
||||
func eglDestroyContext(disp _EGLDisplay, ctx _EGLContext) bool {
|
||||
r, _, _ := _eglDestroyContext.Call(uintptr(disp), uintptr(ctx))
|
||||
return r != 0
|
||||
}
|
||||
|
||||
func eglGetConfigAttrib(disp _EGLDisplay, cfg _EGLConfig, attr _EGLint) (_EGLint, bool) {
|
||||
var val uintptr
|
||||
r, _, _ := _eglGetConfigAttrib.Call(uintptr(disp), uintptr(cfg), uintptr(attr), uintptr(unsafe.Pointer(&val)))
|
||||
return _EGLint(val), r != 0
|
||||
}
|
||||
|
||||
func eglGetDisplay(disp NativeDisplayType) _EGLDisplay {
|
||||
d, _, _ := _eglGetDisplay.Call(uintptr(disp))
|
||||
return _EGLDisplay(d)
|
||||
}
|
||||
|
||||
func eglGetError() _EGLint {
|
||||
e, _, _ := _eglGetError.Call()
|
||||
return _EGLint(e)
|
||||
}
|
||||
|
||||
func eglInitialize(disp _EGLDisplay) (_EGLint, _EGLint, bool) {
|
||||
var maj, min uintptr
|
||||
r, _, _ := _eglInitialize.Call(uintptr(disp), uintptr(unsafe.Pointer(&maj)), uintptr(unsafe.Pointer(&min)))
|
||||
return _EGLint(maj), _EGLint(min), r != 0
|
||||
}
|
||||
|
||||
func eglMakeCurrent(disp _EGLDisplay, draw, read _EGLSurface, ctx _EGLContext) bool {
|
||||
r, _, _ := _eglMakeCurrent.Call(uintptr(disp), uintptr(draw), uintptr(read), uintptr(ctx))
|
||||
return r != 0
|
||||
}
|
||||
|
||||
func eglReleaseThread() bool {
|
||||
r, _, _ := _eglReleaseThread.Call()
|
||||
return r != 0
|
||||
}
|
||||
|
||||
func eglSwapInterval(disp _EGLDisplay, interval _EGLint) bool {
|
||||
r, _, _ := _eglSwapInterval.Call(uintptr(disp), uintptr(interval))
|
||||
return r != 0
|
||||
}
|
||||
|
||||
func eglSwapBuffers(disp _EGLDisplay, surf _EGLSurface) bool {
|
||||
r, _, _ := _eglSwapBuffers.Call(uintptr(disp), uintptr(surf))
|
||||
return r != 0
|
||||
}
|
||||
|
||||
func eglTerminate(disp _EGLDisplay) bool {
|
||||
r, _, _ := _eglTerminate.Call(uintptr(disp))
|
||||
return r != 0
|
||||
}
|
||||
|
||||
func eglQueryString(disp _EGLDisplay, name _EGLint) string {
|
||||
r, _, _ := _eglQueryString.Call(uintptr(disp), uintptr(name))
|
||||
return syscall.BytePtrToString((*byte)(unsafe.Pointer(r)))
|
||||
}
|
||||
|
||||
func eglWaitClient() bool {
|
||||
r, _, _ := _eglWaitClient.Call()
|
||||
return r != 0
|
||||
}
|
||||
|
||||
// issue34474KeepAlive calls runtime.KeepAlive as a
|
||||
// workaround for golang.org/issue/34474.
|
||||
func issue34474KeepAlive(v any) {
|
||||
runtime.KeepAlive(v)
|
||||
}
|
||||
+177
@@ -0,0 +1,177 @@
|
||||
// SPDX-License-Identifier: Unlicense OR MIT
|
||||
|
||||
/*
|
||||
Package f32 is an internal version of the public package f32 with
|
||||
extra types for internal use.
|
||||
*/
|
||||
package f32
|
||||
|
||||
import (
|
||||
"image"
|
||||
"math"
|
||||
|
||||
"gioui.org/f32"
|
||||
)
|
||||
|
||||
type Point = f32.Point
|
||||
|
||||
type Affine2D = f32.Affine2D
|
||||
|
||||
var NewAffine2D = f32.NewAffine2D
|
||||
|
||||
var AffineId = f32.AffineId
|
||||
|
||||
// A Rectangle contains the points (X, Y) where Min.X <= X < Max.X,
|
||||
// Min.Y <= Y < Max.Y.
|
||||
type Rectangle struct {
|
||||
Min, Max Point
|
||||
}
|
||||
|
||||
// String return a string representation of r.
|
||||
func (r Rectangle) String() string {
|
||||
return r.Min.String() + "-" + r.Max.String()
|
||||
}
|
||||
|
||||
// Rect is a shorthand for Rectangle{Point{x0, y0}, Point{x1, y1}}.
|
||||
// The returned Rectangle has x0 and y0 swapped if necessary so that
|
||||
// it's correctly formed.
|
||||
func Rect(x0, y0, x1, y1 float32) Rectangle {
|
||||
if x0 > x1 {
|
||||
x0, x1 = x1, x0
|
||||
}
|
||||
if y0 > y1 {
|
||||
y0, y1 = y1, y0
|
||||
}
|
||||
return Rectangle{Point{x0, y0}, Point{x1, y1}}
|
||||
}
|
||||
|
||||
// Pt is shorthand for Point{X: x, Y: y}.
|
||||
var Pt = f32.Pt
|
||||
|
||||
// Size returns r's width and height.
|
||||
func (r Rectangle) Size() Point {
|
||||
return Point{X: r.Dx(), Y: r.Dy()}
|
||||
}
|
||||
|
||||
// Dx returns r's width.
|
||||
func (r Rectangle) Dx() float32 {
|
||||
return r.Max.X - r.Min.X
|
||||
}
|
||||
|
||||
// Dy returns r's Height.
|
||||
func (r Rectangle) Dy() float32 {
|
||||
return r.Max.Y - r.Min.Y
|
||||
}
|
||||
|
||||
// Intersect returns the intersection of r and s.
|
||||
func (r Rectangle) Intersect(s Rectangle) Rectangle {
|
||||
if r.Min.X < s.Min.X {
|
||||
r.Min.X = s.Min.X
|
||||
}
|
||||
if r.Min.Y < s.Min.Y {
|
||||
r.Min.Y = s.Min.Y
|
||||
}
|
||||
if r.Max.X > s.Max.X {
|
||||
r.Max.X = s.Max.X
|
||||
}
|
||||
if r.Max.Y > s.Max.Y {
|
||||
r.Max.Y = s.Max.Y
|
||||
}
|
||||
if r.Empty() {
|
||||
return Rectangle{}
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// Union returns the union of r and s.
|
||||
func (r Rectangle) Union(s Rectangle) Rectangle {
|
||||
if r.Empty() {
|
||||
return s
|
||||
}
|
||||
if s.Empty() {
|
||||
return r
|
||||
}
|
||||
if r.Min.X > s.Min.X {
|
||||
r.Min.X = s.Min.X
|
||||
}
|
||||
if r.Min.Y > s.Min.Y {
|
||||
r.Min.Y = s.Min.Y
|
||||
}
|
||||
if r.Max.X < s.Max.X {
|
||||
r.Max.X = s.Max.X
|
||||
}
|
||||
if r.Max.Y < s.Max.Y {
|
||||
r.Max.Y = s.Max.Y
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// Canon returns the canonical version of r, where Min is to
|
||||
// the upper left of Max.
|
||||
func (r Rectangle) Canon() Rectangle {
|
||||
if r.Max.X < r.Min.X {
|
||||
r.Min.X, r.Max.X = r.Max.X, r.Min.X
|
||||
}
|
||||
if r.Max.Y < r.Min.Y {
|
||||
r.Min.Y, r.Max.Y = r.Max.Y, r.Min.Y
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// Empty reports whether r represents the empty area.
|
||||
func (r Rectangle) Empty() bool {
|
||||
return r.Min.X >= r.Max.X || r.Min.Y >= r.Max.Y
|
||||
}
|
||||
|
||||
// Add offsets r with the vector p.
|
||||
func (r Rectangle) Add(p Point) Rectangle {
|
||||
return Rectangle{
|
||||
Point{r.Min.X + p.X, r.Min.Y + p.Y},
|
||||
Point{r.Max.X + p.X, r.Max.Y + p.Y},
|
||||
}
|
||||
}
|
||||
|
||||
// Sub offsets r with the vector -p.
|
||||
func (r Rectangle) Sub(p Point) Rectangle {
|
||||
return Rectangle{
|
||||
Point{r.Min.X - p.X, r.Min.Y - p.Y},
|
||||
Point{r.Max.X - p.X, r.Max.Y - p.Y},
|
||||
}
|
||||
}
|
||||
|
||||
// Round returns the smallest integer rectangle that
|
||||
// contains r.
|
||||
func (r Rectangle) Round() image.Rectangle {
|
||||
return image.Rectangle{
|
||||
Min: image.Point{
|
||||
X: int(floor(r.Min.X)),
|
||||
Y: int(floor(r.Min.Y)),
|
||||
},
|
||||
Max: image.Point{
|
||||
X: int(ceil(r.Max.X)),
|
||||
Y: int(ceil(r.Max.Y)),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// fRect converts a rectangle to a f32internal.Rectangle.
|
||||
func FRect(r image.Rectangle) Rectangle {
|
||||
return Rectangle{
|
||||
Min: FPt(r.Min), Max: FPt(r.Max),
|
||||
}
|
||||
}
|
||||
|
||||
// Fpt converts an point to a f32.Point.
|
||||
func FPt(p image.Point) Point {
|
||||
return Point{
|
||||
X: float32(p.X), Y: float32(p.Y),
|
||||
}
|
||||
}
|
||||
|
||||
func ceil(v float32) int {
|
||||
return int(math.Ceil(float64(v)))
|
||||
}
|
||||
|
||||
func floor(v float32) int {
|
||||
return int(math.Floor(float64(v)))
|
||||
}
|
||||
+191
@@ -0,0 +1,191 @@
|
||||
// SPDX-License-Identifier: Unlicense OR MIT
|
||||
|
||||
package f32color
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
"math"
|
||||
)
|
||||
|
||||
//go:generate go run ./f32colorgen -out tables.go
|
||||
|
||||
// RGBA is a 32 bit floating point linear premultiplied color space.
|
||||
type RGBA struct {
|
||||
R, G, B, A float32
|
||||
}
|
||||
|
||||
// Array returns rgba values in a [4]float32 array.
|
||||
func (rgba RGBA) Array() [4]float32 {
|
||||
return [4]float32{rgba.R, rgba.G, rgba.B, rgba.A}
|
||||
}
|
||||
|
||||
// Float32 returns r, g, b, a values.
|
||||
func (col RGBA) Float32() (r, g, b, a float32) {
|
||||
return col.R, col.G, col.B, col.A
|
||||
}
|
||||
|
||||
// SRGBA converts from linear to sRGB color space.
|
||||
func (col RGBA) SRGB() color.NRGBA {
|
||||
if col.A == 0 {
|
||||
return color.NRGBA{}
|
||||
}
|
||||
return color.NRGBA{
|
||||
R: uint8(linearTosRGB(col.R/col.A)*255 + .5),
|
||||
G: uint8(linearTosRGB(col.G/col.A)*255 + .5),
|
||||
B: uint8(linearTosRGB(col.B/col.A)*255 + .5),
|
||||
A: uint8(col.A*255 + .5),
|
||||
}
|
||||
}
|
||||
|
||||
// Luminance calculates the relative luminance of a linear RGBA color.
|
||||
// Normalized to 0 for black and 1 for white.
|
||||
//
|
||||
// See https://www.w3.org/TR/WCAG20/#relativeluminancedef for more details
|
||||
func (col RGBA) Luminance() float32 {
|
||||
return 0.2126*col.R + 0.7152*col.G + 0.0722*col.B
|
||||
}
|
||||
|
||||
// Opaque returns the color without alpha component.
|
||||
func (col RGBA) Opaque() RGBA {
|
||||
col.A = 1.0
|
||||
return col
|
||||
}
|
||||
|
||||
// LinearFromSRGB converts from col in the sRGB colorspace to RGBA.
|
||||
func LinearFromSRGB(col color.NRGBA) RGBA {
|
||||
af := float32(col.A) / 0xFF
|
||||
return RGBA{
|
||||
R: srgb8ToLinear[col.R] * af, // sRGBToLinear(float32(col.R)/0xff) * af,
|
||||
G: srgb8ToLinear[col.G] * af, // sRGBToLinear(float32(col.G)/0xff) * af,
|
||||
B: srgb8ToLinear[col.B] * af, // sRGBToLinear(float32(col.B)/0xff) * af,
|
||||
A: af,
|
||||
}
|
||||
}
|
||||
|
||||
// NRGBAToRGBA converts from non-premultiplied sRGB color to premultiplied sRGB color.
|
||||
//
|
||||
// Each component in the result is `sRGBToLinear(c * alpha)`, where `c`
|
||||
// is the linear color.
|
||||
func NRGBAToRGBA(col color.NRGBA) color.RGBA {
|
||||
if col.A == 0xFF {
|
||||
return color.RGBA(col)
|
||||
}
|
||||
c := LinearFromSRGB(col)
|
||||
return color.RGBA{
|
||||
R: uint8(linearTosRGB(c.R)*255 + .5),
|
||||
G: uint8(linearTosRGB(c.G)*255 + .5),
|
||||
B: uint8(linearTosRGB(c.B)*255 + .5),
|
||||
A: col.A,
|
||||
}
|
||||
}
|
||||
|
||||
// NRGBAToLinearRGBA converts from non-premultiplied sRGB color to premultiplied linear RGBA color.
|
||||
//
|
||||
// Each component in the result is `c * alpha`, where `c` is the linear color.
|
||||
func NRGBAToLinearRGBA(col color.NRGBA) color.RGBA {
|
||||
if col.A == 0xFF {
|
||||
return color.RGBA(col)
|
||||
}
|
||||
c := LinearFromSRGB(col)
|
||||
return color.RGBA{
|
||||
R: uint8(c.R*255 + .5),
|
||||
G: uint8(c.G*255 + .5),
|
||||
B: uint8(c.B*255 + .5),
|
||||
A: col.A,
|
||||
}
|
||||
}
|
||||
|
||||
// RGBAToNRGBA converts from premultiplied sRGB color to non-premultiplied sRGB color.
|
||||
func RGBAToNRGBA(col color.RGBA) color.NRGBA {
|
||||
if col.A == 0xFF {
|
||||
return color.NRGBA(col)
|
||||
}
|
||||
|
||||
linear := RGBA{
|
||||
R: sRGBToLinear(float32(col.R) / 0xff),
|
||||
G: sRGBToLinear(float32(col.G) / 0xff),
|
||||
B: sRGBToLinear(float32(col.B) / 0xff),
|
||||
A: float32(col.A) / 0xff,
|
||||
}
|
||||
|
||||
return linear.SRGB()
|
||||
}
|
||||
|
||||
// linearTosRGB transforms color value from linear to sRGB.
|
||||
func linearTosRGB(c float32) float32 {
|
||||
// Formula from EXT_sRGB.
|
||||
switch {
|
||||
case c <= 0:
|
||||
return 0
|
||||
case 0 < c && c < 0.0031308:
|
||||
return 12.92 * c
|
||||
case 0.0031308 <= c && c < 1:
|
||||
return 1.055*float32(math.Pow(float64(c), 0.41666)) - 0.055
|
||||
}
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
// sRGBToLinear transforms color value from sRGB to linear.
|
||||
func sRGBToLinear(c float32) float32 {
|
||||
// Formula from EXT_sRGB.
|
||||
if c <= 0.04045 {
|
||||
return c / 12.92
|
||||
} else {
|
||||
return float32(math.Pow(float64((c+0.055)/1.055), 2.4))
|
||||
}
|
||||
}
|
||||
|
||||
// MulAlpha applies the alpha to the color.
|
||||
func MulAlpha(c color.NRGBA, alpha uint8) color.NRGBA {
|
||||
c.A = uint8(uint32(c.A) * uint32(alpha) / 0xFF)
|
||||
return c
|
||||
}
|
||||
|
||||
// Disabled blends color towards the luminance and multiplies alpha.
|
||||
// Blending towards luminance will desaturate the color.
|
||||
// Multiplying alpha blends the color together more with the background.
|
||||
func Disabled(c color.NRGBA) (d color.NRGBA) {
|
||||
const r = 80 // blend ratio
|
||||
lum := approxLuminance(c)
|
||||
d = mix(c, color.NRGBA{A: c.A, R: lum, G: lum, B: lum}, r)
|
||||
d = MulAlpha(d, 128+32)
|
||||
return
|
||||
}
|
||||
|
||||
// Hovered blends dark colors towards white, and light colors towards
|
||||
// black. It is approximate because it operates in non-linear sRGB space.
|
||||
func Hovered(c color.NRGBA) (h color.NRGBA) {
|
||||
if c.A == 0 {
|
||||
// Provide a reasonable default for transparent widgets.
|
||||
return color.NRGBA{A: 0x44, R: 0x88, G: 0x88, B: 0x88}
|
||||
}
|
||||
const ratio = 0x20
|
||||
m := color.NRGBA{R: 0xff, G: 0xff, B: 0xff, A: c.A}
|
||||
if approxLuminance(c) > 128 {
|
||||
m = color.NRGBA{A: c.A}
|
||||
}
|
||||
return mix(m, c, ratio)
|
||||
}
|
||||
|
||||
// mix mixes c1 and c2 weighted by (1 - a/256) and a/256 respectively.
|
||||
func mix(c1, c2 color.NRGBA, a uint8) color.NRGBA {
|
||||
ai := int(a)
|
||||
return color.NRGBA{
|
||||
R: byte((int(c1.R)*ai + int(c2.R)*(256-ai)) / 256),
|
||||
G: byte((int(c1.G)*ai + int(c2.G)*(256-ai)) / 256),
|
||||
B: byte((int(c1.B)*ai + int(c2.B)*(256-ai)) / 256),
|
||||
A: byte((int(c1.A)*ai + int(c2.A)*(256-ai)) / 256),
|
||||
}
|
||||
}
|
||||
|
||||
// approxLuminance is a fast approximate version of RGBA.Luminance.
|
||||
func approxLuminance(c color.NRGBA) byte {
|
||||
const (
|
||||
r = 13933 // 0.2126 * 256 * 256
|
||||
g = 46871 // 0.7152 * 256 * 256
|
||||
b = 4732 // 0.0722 * 256 * 256
|
||||
t = r + g + b
|
||||
)
|
||||
return byte((r*int(c.R) + g*int(c.G) + b*int(c.B)) / t)
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
// SPDX-License-Identifier: Unlicense OR MIT
|
||||
|
||||
// Code generated by f32colorgen. DO NOT EDIT.
|
||||
|
||||
package f32color
|
||||
|
||||
// table corresponds to sRGBToLinear(float32(index)/0xff)
|
||||
var srgb8ToLinear = [...]float32{
|
||||
0, 0.000303527, 0.000607054, 0.000910581, 0.001214108, 0.001517635, 0.001821162, 0.0021246888, 0.002428216, 0.002731743, 0.00303527, 0.0033465363, 0.0036765079, 0.004024718, 0.004391443, 0.004776954,
|
||||
0.005181518, 0.0056053926, 0.006048834, 0.0065120924, 0.0069954116, 0.007499033, 0.008023194, 0.008568126, 0.009134059, 0.00972122, 0.010329825, 0.010960096, 0.011612247, 0.012286489, 0.0129830325, 0.013702083,
|
||||
0.014443846, 0.015208517, 0.015996296, 0.016807377, 0.017641956, 0.01850022, 0.019382365, 0.020288566, 0.021219013, 0.022173887, 0.023153368, 0.024157634, 0.025186861, 0.026241226, 0.027320895, 0.028426042,
|
||||
0.029556837, 0.030713446, 0.031896036, 0.033104766, 0.03433981, 0.035601318, 0.03688945, 0.03820437, 0.039546244, 0.040915202, 0.042311415, 0.043735035, 0.04518621, 0.04666509, 0.048171826, 0.049706567,
|
||||
0.05126947, 0.05286066, 0.05448029, 0.056128502, 0.05780544, 0.059511248, 0.06124608, 0.06301004, 0.06480329, 0.06662596, 0.06847819, 0.07036012, 0.07227187, 0.07421359, 0.0761854, 0.078187436,
|
||||
0.080219835, 0.08228272, 0.08437622, 0.08650047, 0.08865561, 0.09084174, 0.09305899, 0.09530749, 0.09758737, 0.09989875, 0.102241755, 0.10461651, 0.10702312, 0.10946173, 0.11193245, 0.11443539,
|
||||
0.11697068, 0.11953844, 0.122138806, 0.12477185, 0.12743771, 0.1301365, 0.13286835, 0.13563335, 0.13843164, 0.14126332, 0.14412849, 0.14702728, 0.1499598, 0.15292618, 0.15592648, 0.15896088,
|
||||
0.16202942, 0.16513222, 0.16826941, 0.17144111, 0.1746474, 0.17788842, 0.18116425, 0.18447499, 0.18782078, 0.19120169, 0.19461782, 0.19806932, 0.20155625, 0.20507872, 0.20863685, 0.21223074,
|
||||
0.21586055, 0.21952623, 0.223228, 0.2269659, 0.23074009, 0.23455067, 0.23839766, 0.24228121, 0.24620141, 0.25015837, 0.25415218, 0.25818294, 0.26225075, 0.2663557, 0.27049786, 0.2746774,
|
||||
0.27889434, 0.28314883, 0.2874409, 0.29177073, 0.29613835, 0.30054384, 0.30498737, 0.30946898, 0.31398878, 0.31854683, 0.32314327, 0.32777816, 0.33245158, 0.33716366, 0.34191447, 0.3467041,
|
||||
0.35153273, 0.35640025, 0.3613069, 0.36625272, 0.37123778, 0.37626222, 0.3813261, 0.38642955, 0.39157256, 0.39675534, 0.40197787, 0.4072403, 0.4125427, 0.41788515, 0.42326775, 0.42869058,
|
||||
0.4341537, 0.43965724, 0.44520128, 0.45078585, 0.4564111, 0.46207705, 0.46778387, 0.47353154, 0.47932023, 0.48515, 0.4910209, 0.49693304, 0.5028866, 0.50888145, 0.5149178, 0.5209957,
|
||||
0.5271153, 0.53327656, 0.5394796, 0.5457246, 0.55201155, 0.5583405, 0.56471163, 0.5711249, 0.5775806, 0.58407855, 0.59061897, 0.5972019, 0.6038274, 0.6104957, 0.61720663, 0.6239605,
|
||||
0.6307572, 0.63759696, 0.64447975, 0.6514057, 0.6583749, 0.66538733, 0.6724432, 0.67954254, 0.6866855, 0.6938719, 0.7011021, 0.70837593, 0.71569365, 0.7230553, 0.7304609, 0.73791057,
|
||||
0.74540436, 0.7529423, 0.76052463, 0.7681513, 0.77582234, 0.7835379, 0.79129803, 0.79910284, 0.80695236, 0.8148467, 0.82278585, 0.83076996, 0.8387991, 0.84687334, 0.8549927, 0.8631573,
|
||||
0.8713672, 0.87962234, 0.8879232, 0.89626944, 0.90466136, 0.9130987, 0.92158204, 0.9301109, 0.9386859, 0.9473066, 0.9559735, 0.9646863, 0.9734455, 0.9822506, 0.9911022, 1,
|
||||
}
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
// SPDX-License-Identifier: Unlicense OR MIT
|
||||
|
||||
package fling
|
||||
|
||||
import (
|
||||
"math"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"gioui.org/unit"
|
||||
)
|
||||
|
||||
type Animation struct {
|
||||
// Current offset in pixels.
|
||||
x float32
|
||||
// Initial time.
|
||||
t0 time.Time
|
||||
// Initial velocity in pixels pr second.
|
||||
v0 float32
|
||||
}
|
||||
|
||||
const (
|
||||
// dp/second.
|
||||
minFlingVelocity = unit.Dp(50)
|
||||
maxFlingVelocity = unit.Dp(8000)
|
||||
thresholdVelocity = 1
|
||||
)
|
||||
|
||||
// Start a fling given a starting velocity. Returns whether a
|
||||
// fling was started.
|
||||
func (f *Animation) Start(c unit.Metric, now time.Time, velocity float32) bool {
|
||||
min := float32(c.Dp(minFlingVelocity))
|
||||
v := velocity
|
||||
if -min <= v && v <= min {
|
||||
return false
|
||||
}
|
||||
max := float32(c.Dp(maxFlingVelocity))
|
||||
if v > max {
|
||||
v = max
|
||||
} else if v < -max {
|
||||
v = -max
|
||||
}
|
||||
f.init(now, v)
|
||||
return true
|
||||
}
|
||||
|
||||
func (f *Animation) init(now time.Time, v0 float32) {
|
||||
f.t0 = now
|
||||
f.v0 = v0
|
||||
f.x = 0
|
||||
}
|
||||
|
||||
func (f *Animation) Active() bool {
|
||||
return f.v0 != 0
|
||||
}
|
||||
|
||||
// Tick computes and returns a fling distance since
|
||||
// the last time Tick was called.
|
||||
func (f *Animation) Tick(now time.Time) int {
|
||||
if !f.Active() {
|
||||
return 0
|
||||
}
|
||||
var k float32
|
||||
if runtime.GOOS == "darwin" {
|
||||
k = -2 // iOS
|
||||
} else {
|
||||
k = -4.2 // Android and default
|
||||
}
|
||||
t := now.Sub(f.t0)
|
||||
// The acceleration x''(t) of a point mass with a drag
|
||||
// force, f, proportional with velocity, x'(t), is
|
||||
// governed by the equation
|
||||
//
|
||||
// x''(t) = kx'(t)
|
||||
//
|
||||
// Given the starting position x(0) = 0, the starting
|
||||
// velocity x'(0) = v0, the position is then
|
||||
// given by
|
||||
//
|
||||
// x(t) = v0*e^(k*t)/k - v0/k
|
||||
//
|
||||
ekt := float32(math.Exp(float64(k) * t.Seconds()))
|
||||
x := f.v0*ekt/k - f.v0/k
|
||||
dist := x - f.x
|
||||
idist := int(dist)
|
||||
f.x += float32(idist)
|
||||
// Solving for the velocity x'(t) gives us
|
||||
//
|
||||
// x'(t) = v0*e^(k*t)
|
||||
v := f.v0 * ekt
|
||||
if -thresholdVelocity < v && v < thresholdVelocity {
|
||||
f.v0 = 0
|
||||
}
|
||||
return idist
|
||||
}
|
||||
+332
@@ -0,0 +1,332 @@
|
||||
// SPDX-License-Identifier: Unlicense OR MIT
|
||||
|
||||
package fling
|
||||
|
||||
import (
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Extrapolation computes a 1-dimensional velocity estimate
|
||||
// for a set of timestamped points using the least squares
|
||||
// fit of a 2nd order polynomial. The same method is used
|
||||
// by Android.
|
||||
type Extrapolation struct {
|
||||
// Index into points.
|
||||
idx int
|
||||
// Circular buffer of samples.
|
||||
samples []sample
|
||||
lastValue float32
|
||||
// Pre-allocated cache for samples.
|
||||
cache [historySize]sample
|
||||
|
||||
// Filtered values and times
|
||||
values [historySize]float32
|
||||
times [historySize]float32
|
||||
}
|
||||
|
||||
type sample struct {
|
||||
t time.Duration
|
||||
v float32
|
||||
}
|
||||
|
||||
type matrix struct {
|
||||
rows, cols int
|
||||
data []float32
|
||||
}
|
||||
|
||||
type Estimate struct {
|
||||
Velocity float32
|
||||
Distance float32
|
||||
}
|
||||
|
||||
type coefficients [degree + 1]float32
|
||||
|
||||
const (
|
||||
degree = 2
|
||||
historySize = 20
|
||||
maxAge = 100 * time.Millisecond
|
||||
maxSampleGap = 40 * time.Millisecond
|
||||
)
|
||||
|
||||
// SampleDelta adds a relative sample to the estimation.
|
||||
func (e *Extrapolation) SampleDelta(t time.Duration, delta float32) {
|
||||
val := delta + e.lastValue
|
||||
e.Sample(t, val)
|
||||
}
|
||||
|
||||
// Sample adds an absolute sample to the estimation.
|
||||
func (e *Extrapolation) Sample(t time.Duration, val float32) {
|
||||
e.lastValue = val
|
||||
if e.samples == nil {
|
||||
e.samples = e.cache[:0]
|
||||
}
|
||||
s := sample{
|
||||
t: t,
|
||||
v: val,
|
||||
}
|
||||
if e.idx == len(e.samples) && e.idx < cap(e.samples) {
|
||||
e.samples = append(e.samples, s)
|
||||
} else {
|
||||
e.samples[e.idx] = s
|
||||
}
|
||||
e.idx++
|
||||
if e.idx == cap(e.samples) {
|
||||
e.idx = 0
|
||||
}
|
||||
}
|
||||
|
||||
// Velocity returns an estimate of the implied velocity and
|
||||
// distance for the points sampled, or zero if the estimation method
|
||||
// failed.
|
||||
func (e *Extrapolation) Estimate() Estimate {
|
||||
if len(e.samples) == 0 {
|
||||
return Estimate{}
|
||||
}
|
||||
values := e.values[:0]
|
||||
times := e.times[:0]
|
||||
first := e.get(0)
|
||||
t := first.t
|
||||
// Walk backwards collecting samples.
|
||||
for i := range e.samples {
|
||||
p := e.get(-i)
|
||||
age := first.t - p.t
|
||||
if age >= maxAge || t-p.t >= maxSampleGap {
|
||||
// If the samples are too old or
|
||||
// too much time passed between samples
|
||||
// assume they're not part of the fling.
|
||||
break
|
||||
}
|
||||
t = p.t
|
||||
values = append(values, first.v-p.v)
|
||||
times = append(times, float32((-age).Seconds()))
|
||||
}
|
||||
coef, ok := polyFit(times, values)
|
||||
if !ok {
|
||||
return Estimate{}
|
||||
}
|
||||
dist := values[len(values)-1] - values[0]
|
||||
return Estimate{
|
||||
Velocity: coef[1],
|
||||
Distance: dist,
|
||||
}
|
||||
}
|
||||
|
||||
func (e *Extrapolation) get(i int) sample {
|
||||
idx := (e.idx + i - 1 + len(e.samples)) % len(e.samples)
|
||||
return e.samples[idx]
|
||||
}
|
||||
|
||||
// fit computes the least squares polynomial fit for
|
||||
// the set of points in X, Y. If the fitting fails
|
||||
// because of contradicting or insufficient data,
|
||||
// fit returns false.
|
||||
func polyFit(X, Y []float32) (coefficients, bool) {
|
||||
if len(X) != len(Y) {
|
||||
panic("X and Y lengths differ")
|
||||
}
|
||||
if len(X) <= degree {
|
||||
// Not enough points to fit a curve.
|
||||
return coefficients{}, false
|
||||
}
|
||||
|
||||
// Use a method similar to Android's VelocityTracker.cpp:
|
||||
// https://android.googlesource.com/platform/frameworks/base/+/56a2301/libs/androidfw/VelocityTracker.cpp
|
||||
// where all weights are 1.
|
||||
|
||||
// First, expand the X vector to the matrix A in column-major order.
|
||||
A := newMatrix(degree+1, len(X))
|
||||
for i, x := range X {
|
||||
A.set(0, i, 1)
|
||||
for j := 1; j < A.rows; j++ {
|
||||
A.set(j, i, A.get(j-1, i)*x)
|
||||
}
|
||||
}
|
||||
|
||||
Q, Rt, ok := decomposeQR(A)
|
||||
if !ok {
|
||||
return coefficients{}, false
|
||||
}
|
||||
// Solve R*B = Qt*Y for B, which is then the polynomial coefficients.
|
||||
// Since R is upper triangular, we can proceed from bottom right to
|
||||
// upper left.
|
||||
// https://en.wikipedia.org/wiki/Non-linear_least_squares
|
||||
var B coefficients
|
||||
for i := Q.rows - 1; i >= 0; i-- {
|
||||
B[i] = dot(Q.col(i), Y)
|
||||
for j := Q.rows - 1; j > i; j-- {
|
||||
B[i] -= Rt.get(i, j) * B[j]
|
||||
}
|
||||
B[i] /= Rt.get(i, i)
|
||||
}
|
||||
return B, true
|
||||
}
|
||||
|
||||
// decomposeQR computes and returns Q, Rt where Q*transpose(Rt) = A, if
|
||||
// possible. R is guaranteed to be upper triangular and only the square
|
||||
// part of Rt is returned.
|
||||
func decomposeQR(A *matrix) (*matrix, *matrix, bool) {
|
||||
// Gram-Schmidt QR decompose A where Q*R = A.
|
||||
// https://en.wikipedia.org/wiki/Gram%E2%80%93Schmidt_process
|
||||
Q := newMatrix(A.rows, A.cols) // Column-major.
|
||||
Rt := newMatrix(A.rows, A.rows) // R transposed, row-major.
|
||||
for i := range Q.rows {
|
||||
// Copy A column.
|
||||
for j := range Q.cols {
|
||||
Q.set(i, j, A.get(i, j))
|
||||
}
|
||||
// Subtract projections. Note that int the projection
|
||||
//
|
||||
// proju a = <u, a>/<u, u> u
|
||||
//
|
||||
// the normalized column e replaces u, where <e, e> = 1:
|
||||
//
|
||||
// proje a = <e, a>/<e, e> e = <e, a> e
|
||||
for j := range i {
|
||||
d := dot(Q.col(j), Q.col(i))
|
||||
for k := range Q.cols {
|
||||
Q.set(i, k, Q.get(i, k)-d*Q.get(j, k))
|
||||
}
|
||||
}
|
||||
// Normalize Q columns.
|
||||
n := norm(Q.col(i))
|
||||
if n < 0.000001 {
|
||||
// Degenerate data, no solution.
|
||||
return nil, nil, false
|
||||
}
|
||||
invNorm := 1 / n
|
||||
for j := range Q.cols {
|
||||
Q.set(i, j, Q.get(i, j)*invNorm)
|
||||
}
|
||||
// Update Rt.
|
||||
for j := i; j < Rt.cols; j++ {
|
||||
Rt.set(i, j, dot(Q.col(i), A.col(j)))
|
||||
}
|
||||
}
|
||||
return Q, Rt, true
|
||||
}
|
||||
|
||||
func norm(V []float32) float32 {
|
||||
var n float32
|
||||
for _, v := range V {
|
||||
n += v * v
|
||||
}
|
||||
return float32(math.Sqrt(float64(n)))
|
||||
}
|
||||
|
||||
func dot(V1, V2 []float32) float32 {
|
||||
var d float32
|
||||
for i, v1 := range V1 {
|
||||
d += v1 * V2[i]
|
||||
}
|
||||
return d
|
||||
}
|
||||
|
||||
func newMatrix(rows, cols int) *matrix {
|
||||
return &matrix{
|
||||
rows: rows,
|
||||
cols: cols,
|
||||
data: make([]float32, rows*cols),
|
||||
}
|
||||
}
|
||||
|
||||
func (m *matrix) set(row, col int, v float32) {
|
||||
if row < 0 || row >= m.rows {
|
||||
panic("row out of range")
|
||||
}
|
||||
if col < 0 || col >= m.cols {
|
||||
panic("col out of range")
|
||||
}
|
||||
m.data[row*m.cols+col] = v
|
||||
}
|
||||
|
||||
func (m *matrix) get(row, col int) float32 {
|
||||
if row < 0 || row >= m.rows {
|
||||
panic("row out of range")
|
||||
}
|
||||
if col < 0 || col >= m.cols {
|
||||
panic("col out of range")
|
||||
}
|
||||
return m.data[row*m.cols+col]
|
||||
}
|
||||
|
||||
func (m *matrix) col(c int) []float32 {
|
||||
return m.data[c*m.cols : (c+1)*m.cols]
|
||||
}
|
||||
|
||||
func (m *matrix) approxEqual(m2 *matrix) bool {
|
||||
if m.rows != m2.rows || m.cols != m2.cols {
|
||||
return false
|
||||
}
|
||||
const epsilon = 0.00001
|
||||
for row := range m.rows {
|
||||
for col := range m.cols {
|
||||
d := m2.get(row, col) - m.get(row, col)
|
||||
if d < -epsilon || d > epsilon {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (m *matrix) transpose() *matrix {
|
||||
t := &matrix{
|
||||
rows: m.cols,
|
||||
cols: m.rows,
|
||||
data: make([]float32, len(m.data)),
|
||||
}
|
||||
for i := range m.rows {
|
||||
for j := range m.cols {
|
||||
t.set(j, i, m.get(i, j))
|
||||
}
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func (m *matrix) mul(m2 *matrix) *matrix {
|
||||
if m.rows != m2.cols {
|
||||
panic("mismatched matrices")
|
||||
}
|
||||
mm := &matrix{
|
||||
rows: m.rows,
|
||||
cols: m2.cols,
|
||||
data: make([]float32, m.rows*m2.cols),
|
||||
}
|
||||
for i := range mm.rows {
|
||||
for j := range mm.cols {
|
||||
var v float32
|
||||
for k := range m.rows {
|
||||
v += m.get(k, j) * m2.get(i, k)
|
||||
}
|
||||
mm.set(i, j, v)
|
||||
}
|
||||
}
|
||||
return mm
|
||||
}
|
||||
|
||||
func (m *matrix) String() string {
|
||||
var b strings.Builder
|
||||
for i := range m.rows {
|
||||
for j := range m.cols {
|
||||
v := m.get(i, j)
|
||||
b.WriteString(strconv.FormatFloat(float64(v), 'g', -1, 32))
|
||||
b.WriteString(", ")
|
||||
}
|
||||
b.WriteString("\n")
|
||||
}
|
||||
return b.String()
|
||||
}
|
||||
|
||||
func (c coefficients) approxEqual(c2 coefficients) bool {
|
||||
const epsilon = 0.00001
|
||||
for i, v := range c {
|
||||
d := v - c2[i]
|
||||
if d < -epsilon || d > epsilon {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
+131
@@ -0,0 +1,131 @@
|
||||
// SPDX-License-Identifier: Unlicense OR MIT
|
||||
|
||||
package gl
|
||||
|
||||
type (
|
||||
Attrib uint
|
||||
Enum uint
|
||||
)
|
||||
|
||||
const (
|
||||
ACTIVE_TEXTURE = 0x84E0
|
||||
ALL_BARRIER_BITS = 0xffffffff
|
||||
ARRAY_BUFFER = 0x8892
|
||||
ARRAY_BUFFER_BINDING = 0x8894
|
||||
BACK = 0x0405
|
||||
BLEND = 0xbe2
|
||||
BLEND_DST_RGB = 0x80C8
|
||||
BLEND_SRC_RGB = 0x80C9
|
||||
BLEND_DST_ALPHA = 0x80CA
|
||||
BLEND_SRC_ALPHA = 0x80CB
|
||||
CLAMP_TO_EDGE = 0x812f
|
||||
COLOR_ATTACHMENT0 = 0x8ce0
|
||||
COLOR_BUFFER_BIT = 0x4000
|
||||
COLOR_CLEAR_VALUE = 0x0C22
|
||||
COMPILE_STATUS = 0x8b81
|
||||
COMPUTE_SHADER = 0x91B9
|
||||
CURRENT_PROGRAM = 0x8B8D
|
||||
DEPTH_ATTACHMENT = 0x8d00
|
||||
DEPTH_BUFFER_BIT = 0x100
|
||||
DEPTH_CLEAR_VALUE = 0x0B73
|
||||
DEPTH_COMPONENT16 = 0x81a5
|
||||
DEPTH_COMPONENT24 = 0x81A6
|
||||
DEPTH_COMPONENT32F = 0x8CAC
|
||||
DEPTH_FUNC = 0x0B74
|
||||
DEPTH_TEST = 0xb71
|
||||
DEPTH_WRITEMASK = 0x0B72
|
||||
DRAW_FRAMEBUFFER = 0x8CA9
|
||||
DST_COLOR = 0x306
|
||||
DYNAMIC_DRAW = 0x88E8
|
||||
DYNAMIC_READ = 0x88E9
|
||||
ELEMENT_ARRAY_BUFFER = 0x8893
|
||||
ELEMENT_ARRAY_BUFFER_BINDING = 0x8895
|
||||
EXTENSIONS = 0x1f03
|
||||
FALSE = 0
|
||||
FLOAT = 0x1406
|
||||
FRAGMENT_SHADER = 0x8b30
|
||||
FRAMEBUFFER = 0x8d40
|
||||
FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING = 0x8210
|
||||
FRAMEBUFFER_BINDING = 0x8ca6
|
||||
FRAMEBUFFER_COMPLETE = 0x8cd5
|
||||
FRAMEBUFFER_SRGB = 0x8db9
|
||||
HALF_FLOAT = 0x140b
|
||||
HALF_FLOAT_OES = 0x8d61
|
||||
INFO_LOG_LENGTH = 0x8B84
|
||||
INVALID_INDEX = ^uint(0)
|
||||
GREATER = 0x204
|
||||
GEQUAL = 0x206
|
||||
LINEAR = 0x2601
|
||||
LINEAR_MIPMAP_LINEAR = 0x2703
|
||||
LINK_STATUS = 0x8b82
|
||||
LUMINANCE = 0x1909
|
||||
MAP_READ_BIT = 0x0001
|
||||
MAX_TEXTURE_SIZE = 0xd33
|
||||
NEAREST = 0x2600
|
||||
NO_ERROR = 0x0
|
||||
NUM_EXTENSIONS = 0x821D
|
||||
ONE = 0x1
|
||||
ONE_MINUS_SRC_ALPHA = 0x303
|
||||
PACK_ROW_LENGTH = 0x0D02
|
||||
PROGRAM_BINARY_LENGTH = 0x8741
|
||||
QUERY_RESULT = 0x8866
|
||||
QUERY_RESULT_AVAILABLE = 0x8867
|
||||
R16F = 0x822d
|
||||
R8 = 0x8229
|
||||
READ_FRAMEBUFFER = 0x8ca8
|
||||
READ_FRAMEBUFFER_BINDING = 0x8CAA
|
||||
READ_ONLY = 0x88B8
|
||||
READ_WRITE = 0x88BA
|
||||
RED = 0x1903
|
||||
RENDERER = 0x1F01
|
||||
RENDERBUFFER = 0x8d41
|
||||
RENDERBUFFER_BINDING = 0x8ca7
|
||||
RENDERBUFFER_HEIGHT = 0x8d43
|
||||
RENDERBUFFER_WIDTH = 0x8d42
|
||||
RGB = 0x1907
|
||||
RGBA = 0x1908
|
||||
RGBA8 = 0x8058
|
||||
SHADER_STORAGE_BUFFER = 0x90D2
|
||||
SHADER_STORAGE_BUFFER_BINDING = 0x90D3
|
||||
SHORT = 0x1402
|
||||
SRGB = 0x8c40
|
||||
SRGB_ALPHA_EXT = 0x8c42
|
||||
SRGB8 = 0x8c41
|
||||
SRGB8_ALPHA8 = 0x8c43
|
||||
STATIC_DRAW = 0x88e4
|
||||
STENCIL_BUFFER_BIT = 0x00000400
|
||||
TEXTURE_2D = 0xde1
|
||||
TEXTURE_BINDING_2D = 0x8069
|
||||
TEXTURE_MAG_FILTER = 0x2800
|
||||
TEXTURE_MIN_FILTER = 0x2801
|
||||
TEXTURE_WRAP_S = 0x2802
|
||||
TEXTURE_WRAP_T = 0x2803
|
||||
TEXTURE0 = 0x84c0
|
||||
TEXTURE1 = 0x84c1
|
||||
TRIANGLE_STRIP = 0x5
|
||||
TRIANGLES = 0x4
|
||||
TRUE = 1
|
||||
UNIFORM_BUFFER = 0x8A11
|
||||
UNIFORM_BUFFER_BINDING = 0x8A28
|
||||
UNPACK_ALIGNMENT = 0xcf5
|
||||
UNPACK_ROW_LENGTH = 0x0CF2
|
||||
UNSIGNED_BYTE = 0x1401
|
||||
UNSIGNED_SHORT = 0x1403
|
||||
VIEWPORT = 0x0BA2
|
||||
VERSION = 0x1f02
|
||||
VERTEX_ARRAY_BINDING = 0x85B5
|
||||
VERTEX_SHADER = 0x8b31
|
||||
VERTEX_ATTRIB_ARRAY_BUFFER_BINDING = 0x889F
|
||||
VERTEX_ATTRIB_ARRAY_ENABLED = 0x8622
|
||||
VERTEX_ATTRIB_ARRAY_POINTER = 0x8645
|
||||
VERTEX_ATTRIB_ARRAY_NORMALIZED = 0x886A
|
||||
VERTEX_ATTRIB_ARRAY_SIZE = 0x8623
|
||||
VERTEX_ATTRIB_ARRAY_STRIDE = 0x8624
|
||||
VERTEX_ATTRIB_ARRAY_TYPE = 0x8625
|
||||
WRITE_ONLY = 0x88B9
|
||||
ZERO = 0x0
|
||||
|
||||
// EXT_disjoint_timer_query
|
||||
TIME_ELAPSED_EXT = 0x88BF
|
||||
GPU_DISJOINT_EXT = 0x8FBB
|
||||
)
|
||||
+748
@@ -0,0 +1,748 @@
|
||||
// SPDX-License-Identifier: Unlicense OR MIT
|
||||
|
||||
package gl
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
"syscall/js"
|
||||
)
|
||||
|
||||
type Functions struct {
|
||||
Ctx js.Value
|
||||
EXT_disjoint_timer_query js.Value
|
||||
EXT_disjoint_timer_query_webgl2 js.Value
|
||||
|
||||
// Cached reference to the Uint8Array JS type.
|
||||
uint8Array js.Value
|
||||
|
||||
// Cached JS arrays.
|
||||
arrayBuf js.Value
|
||||
int32Buf js.Value
|
||||
|
||||
isWebGL2 bool
|
||||
|
||||
_getExtension js.Value
|
||||
_activeTexture js.Value
|
||||
_attachShader js.Value
|
||||
_beginQuery js.Value
|
||||
_beginQueryEXT js.Value
|
||||
_bindAttribLocation js.Value
|
||||
_bindBuffer js.Value
|
||||
_bindBufferBase js.Value
|
||||
_bindFramebuffer js.Value
|
||||
_bindRenderbuffer js.Value
|
||||
_bindTexture js.Value
|
||||
_blendEquation js.Value
|
||||
_blendFunc js.Value
|
||||
_bufferData js.Value
|
||||
_bufferSubData js.Value
|
||||
_checkFramebufferStatus js.Value
|
||||
_clear js.Value
|
||||
_clearColor js.Value
|
||||
_clearDepth js.Value
|
||||
_compileShader js.Value
|
||||
_copyTexSubImage2D js.Value
|
||||
_createBuffer js.Value
|
||||
_createFramebuffer js.Value
|
||||
_createProgram js.Value
|
||||
_createQuery js.Value
|
||||
_createRenderbuffer js.Value
|
||||
_createShader js.Value
|
||||
_createTexture js.Value
|
||||
_deleteBuffer js.Value
|
||||
_deleteFramebuffer js.Value
|
||||
_deleteProgram js.Value
|
||||
_deleteQuery js.Value
|
||||
_deleteQueryEXT js.Value
|
||||
_deleteShader js.Value
|
||||
_deleteRenderbuffer js.Value
|
||||
_deleteTexture js.Value
|
||||
_depthFunc js.Value
|
||||
_depthMask js.Value
|
||||
_disableVertexAttribArray js.Value
|
||||
_disable js.Value
|
||||
_drawArrays js.Value
|
||||
_drawElements js.Value
|
||||
_enable js.Value
|
||||
_enableVertexAttribArray js.Value
|
||||
_endQuery js.Value
|
||||
_endQueryEXT js.Value
|
||||
_finish js.Value
|
||||
_flush js.Value
|
||||
_framebufferRenderbuffer js.Value
|
||||
_framebufferTexture2D js.Value
|
||||
_generateMipmap js.Value
|
||||
_getRenderbufferParameteri js.Value
|
||||
_getFramebufferAttachmentParameter js.Value
|
||||
_getParameter js.Value
|
||||
_getIndexedParameter js.Value
|
||||
_getProgramParameter js.Value
|
||||
_getProgramInfoLog js.Value
|
||||
_getQueryParameter js.Value
|
||||
_getQueryObjectEXT js.Value
|
||||
_getShaderParameter js.Value
|
||||
_getShaderInfoLog js.Value
|
||||
_getSupportedExtensions js.Value
|
||||
_getUniformBlockIndex js.Value
|
||||
_getUniformLocation js.Value
|
||||
_getVertexAttrib js.Value
|
||||
_getVertexAttribOffset js.Value
|
||||
_invalidateFramebuffer js.Value
|
||||
_isEnabled js.Value
|
||||
_linkProgram js.Value
|
||||
_pixelStorei js.Value
|
||||
_renderbufferStorage js.Value
|
||||
_readPixels js.Value
|
||||
_scissor js.Value
|
||||
_shaderSource js.Value
|
||||
_texImage2D js.Value
|
||||
_texStorage2D js.Value
|
||||
_texSubImage2D js.Value
|
||||
_texParameteri js.Value
|
||||
_uniformBlockBinding js.Value
|
||||
_uniform1f js.Value
|
||||
_uniform1i js.Value
|
||||
_uniform2f js.Value
|
||||
_uniform3f js.Value
|
||||
_uniform4f js.Value
|
||||
_useProgram js.Value
|
||||
_vertexAttribPointer js.Value
|
||||
_viewport js.Value
|
||||
}
|
||||
|
||||
type Context js.Value
|
||||
|
||||
func NewFunctions(ctx Context, forceES bool) (*Functions, error) {
|
||||
webgl := js.Value(ctx)
|
||||
f := &Functions{
|
||||
Ctx: webgl,
|
||||
uint8Array: js.Global().Get("Uint8Array"),
|
||||
_getExtension: _bind(webgl, `getExtension`),
|
||||
_activeTexture: _bind(webgl, `activeTexture`),
|
||||
_attachShader: _bind(webgl, `attachShader`),
|
||||
_beginQuery: _bind(webgl, `beginQuery`),
|
||||
_beginQueryEXT: _bind(webgl, `beginQueryEXT`),
|
||||
_bindAttribLocation: _bind(webgl, `bindAttribLocation`),
|
||||
_bindBuffer: _bind(webgl, `bindBuffer`),
|
||||
_bindBufferBase: _bind(webgl, `bindBufferBase`),
|
||||
_bindFramebuffer: _bind(webgl, `bindFramebuffer`),
|
||||
_bindRenderbuffer: _bind(webgl, `bindRenderbuffer`),
|
||||
_bindTexture: _bind(webgl, `bindTexture`),
|
||||
_blendEquation: _bind(webgl, `blendEquation`),
|
||||
_blendFunc: _bind(webgl, `blendFunc`),
|
||||
_bufferData: _bind(webgl, `bufferData`),
|
||||
_bufferSubData: _bind(webgl, `bufferSubData`),
|
||||
_checkFramebufferStatus: _bind(webgl, `checkFramebufferStatus`),
|
||||
_clear: _bind(webgl, `clear`),
|
||||
_clearColor: _bind(webgl, `clearColor`),
|
||||
_clearDepth: _bind(webgl, `clearDepth`),
|
||||
_compileShader: _bind(webgl, `compileShader`),
|
||||
_copyTexSubImage2D: _bind(webgl, `copyTexSubImage2D`),
|
||||
_createBuffer: _bind(webgl, `createBuffer`),
|
||||
_createFramebuffer: _bind(webgl, `createFramebuffer`),
|
||||
_createProgram: _bind(webgl, `createProgram`),
|
||||
_createQuery: _bind(webgl, `createQuery`),
|
||||
_createRenderbuffer: _bind(webgl, `createRenderbuffer`),
|
||||
_createShader: _bind(webgl, `createShader`),
|
||||
_createTexture: _bind(webgl, `createTexture`),
|
||||
_deleteBuffer: _bind(webgl, `deleteBuffer`),
|
||||
_deleteFramebuffer: _bind(webgl, `deleteFramebuffer`),
|
||||
_deleteProgram: _bind(webgl, `deleteProgram`),
|
||||
_deleteQuery: _bind(webgl, `deleteQuery`),
|
||||
_deleteQueryEXT: _bind(webgl, `deleteQueryEXT`),
|
||||
_deleteShader: _bind(webgl, `deleteShader`),
|
||||
_deleteRenderbuffer: _bind(webgl, `deleteRenderbuffer`),
|
||||
_deleteTexture: _bind(webgl, `deleteTexture`),
|
||||
_depthFunc: _bind(webgl, `depthFunc`),
|
||||
_depthMask: _bind(webgl, `depthMask`),
|
||||
_disableVertexAttribArray: _bind(webgl, `disableVertexAttribArray`),
|
||||
_disable: _bind(webgl, `disable`),
|
||||
_drawArrays: _bind(webgl, `drawArrays`),
|
||||
_drawElements: _bind(webgl, `drawElements`),
|
||||
_enable: _bind(webgl, `enable`),
|
||||
_enableVertexAttribArray: _bind(webgl, `enableVertexAttribArray`),
|
||||
_endQuery: _bind(webgl, `endQuery`),
|
||||
_endQueryEXT: _bind(webgl, `endQueryEXT`),
|
||||
_finish: _bind(webgl, `finish`),
|
||||
_flush: _bind(webgl, `flush`),
|
||||
_framebufferRenderbuffer: _bind(webgl, `framebufferRenderbuffer`),
|
||||
_framebufferTexture2D: _bind(webgl, `framebufferTexture2D`),
|
||||
_generateMipmap: _bind(webgl, `generateMipmap`),
|
||||
_getRenderbufferParameteri: _bind(webgl, `getRenderbufferParameteri`),
|
||||
_getFramebufferAttachmentParameter: _bind(webgl, `getFramebufferAttachmentParameter`),
|
||||
_getParameter: _bind(webgl, `getParameter`),
|
||||
_getIndexedParameter: _bind(webgl, `getIndexedParameter`),
|
||||
_getProgramParameter: _bind(webgl, `getProgramParameter`),
|
||||
_getProgramInfoLog: _bind(webgl, `getProgramInfoLog`),
|
||||
_getQueryParameter: _bind(webgl, `getQueryParameter`),
|
||||
_getQueryObjectEXT: _bind(webgl, `getQueryObjectEXT`),
|
||||
_getShaderParameter: _bind(webgl, `getShaderParameter`),
|
||||
_getShaderInfoLog: _bind(webgl, `getShaderInfoLog`),
|
||||
_getSupportedExtensions: _bind(webgl, `getSupportedExtensions`),
|
||||
_getUniformBlockIndex: _bind(webgl, `getUniformBlockIndex`),
|
||||
_getUniformLocation: _bind(webgl, `getUniformLocation`),
|
||||
_getVertexAttrib: _bind(webgl, `getVertexAttrib`),
|
||||
_getVertexAttribOffset: _bind(webgl, `getVertexAttribOffset`),
|
||||
_invalidateFramebuffer: _bind(webgl, `invalidateFramebuffer`),
|
||||
_isEnabled: _bind(webgl, `isEnabled`),
|
||||
_linkProgram: _bind(webgl, `linkProgram`),
|
||||
_pixelStorei: _bind(webgl, `pixelStorei`),
|
||||
_renderbufferStorage: _bind(webgl, `renderbufferStorage`),
|
||||
_readPixels: _bind(webgl, `readPixels`),
|
||||
_scissor: _bind(webgl, `scissor`),
|
||||
_shaderSource: _bind(webgl, `shaderSource`),
|
||||
_texImage2D: _bind(webgl, `texImage2D`),
|
||||
_texStorage2D: _bind(webgl, `texStorage2D`),
|
||||
_texSubImage2D: _bind(webgl, `texSubImage2D`),
|
||||
_texParameteri: _bind(webgl, `texParameteri`),
|
||||
_uniformBlockBinding: _bind(webgl, `uniformBlockBinding`),
|
||||
_uniform1f: _bind(webgl, `uniform1f`),
|
||||
_uniform1i: _bind(webgl, `uniform1i`),
|
||||
_uniform2f: _bind(webgl, `uniform2f`),
|
||||
_uniform3f: _bind(webgl, `uniform3f`),
|
||||
_uniform4f: _bind(webgl, `uniform4f`),
|
||||
_useProgram: _bind(webgl, `useProgram`),
|
||||
_vertexAttribPointer: _bind(webgl, `vertexAttribPointer`),
|
||||
_viewport: _bind(webgl, `viewport`),
|
||||
}
|
||||
if err := f.Init(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return f, nil
|
||||
}
|
||||
|
||||
func _bind(ctx js.Value, p string) js.Value {
|
||||
if o := ctx.Get(p); o.Truthy() {
|
||||
return o.Call("bind", ctx)
|
||||
}
|
||||
return js.Undefined()
|
||||
}
|
||||
|
||||
func (f *Functions) Init() error {
|
||||
webgl2Class := js.Global().Get("WebGL2RenderingContext")
|
||||
f.isWebGL2 = !webgl2Class.IsUndefined() && f.Ctx.InstanceOf(webgl2Class)
|
||||
if !f.isWebGL2 {
|
||||
f.EXT_disjoint_timer_query = f.getExtension("EXT_disjoint_timer_query")
|
||||
if f.getExtension("OES_texture_half_float").IsNull() && f.getExtension("OES_texture_float").IsNull() {
|
||||
return errors.New("gl: no support for neither OES_texture_half_float nor OES_texture_float")
|
||||
}
|
||||
if f.getExtension("EXT_sRGB").IsNull() {
|
||||
return errors.New("gl: EXT_sRGB not supported")
|
||||
}
|
||||
} else {
|
||||
// WebGL2 extensions.
|
||||
f.EXT_disjoint_timer_query_webgl2 = f.getExtension("EXT_disjoint_timer_query_webgl2")
|
||||
if f.getExtension("EXT_color_buffer_half_float").IsNull() && f.getExtension("EXT_color_buffer_float").IsNull() {
|
||||
return errors.New("gl: no support for neither EXT_color_buffer_half_float nor EXT_color_buffer_float")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *Functions) getExtension(name string) js.Value {
|
||||
return f._getExtension.Invoke(name)
|
||||
}
|
||||
|
||||
func (f *Functions) ActiveTexture(t Enum) {
|
||||
f._activeTexture.Invoke(int(t))
|
||||
}
|
||||
|
||||
func (f *Functions) AttachShader(p Program, s Shader) {
|
||||
f._attachShader.Invoke(js.Value(p), js.Value(s))
|
||||
}
|
||||
|
||||
func (f *Functions) BeginQuery(target Enum, query Query) {
|
||||
if !f.EXT_disjoint_timer_query_webgl2.IsNull() {
|
||||
f._beginQuery.Invoke(int(target), js.Value(query))
|
||||
} else {
|
||||
f.EXT_disjoint_timer_query.Call("beginQueryEXT", int(target), js.Value(query))
|
||||
}
|
||||
}
|
||||
|
||||
func (f *Functions) BindAttribLocation(p Program, a Attrib, name string) {
|
||||
f._bindAttribLocation.Invoke(js.Value(p), int(a), name)
|
||||
}
|
||||
|
||||
func (f *Functions) BindBuffer(target Enum, b Buffer) {
|
||||
f._bindBuffer.Invoke(int(target), js.Value(b))
|
||||
}
|
||||
|
||||
func (f *Functions) BindBufferBase(target Enum, index int, b Buffer) {
|
||||
f._bindBufferBase.Invoke(int(target), index, js.Value(b))
|
||||
}
|
||||
|
||||
func (f *Functions) BindFramebuffer(target Enum, fb Framebuffer) {
|
||||
f._bindFramebuffer.Invoke(int(target), js.Value(fb))
|
||||
}
|
||||
|
||||
func (f *Functions) BindRenderbuffer(target Enum, rb Renderbuffer) {
|
||||
f._bindRenderbuffer.Invoke(int(target), js.Value(rb))
|
||||
}
|
||||
|
||||
func (f *Functions) BindTexture(target Enum, t Texture) {
|
||||
f._bindTexture.Invoke(int(target), js.Value(t))
|
||||
}
|
||||
|
||||
func (f *Functions) BindImageTexture(unit int, t Texture, level int, layered bool, layer int, access, format Enum) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (f *Functions) BindVertexArray(a VertexArray) {
|
||||
panic("not supported")
|
||||
}
|
||||
|
||||
func (f *Functions) BlendEquation(mode Enum) {
|
||||
f._blendEquation.Invoke(int(mode))
|
||||
}
|
||||
|
||||
func (f *Functions) BlendFuncSeparate(srcRGB, dstRGB, srcA, dstA Enum) {
|
||||
f._blendFunc.Invoke(int(srcRGB), int(dstRGB), int(srcA), int(dstA))
|
||||
}
|
||||
|
||||
func (f *Functions) BufferData(target Enum, size int, usage Enum, data []byte) {
|
||||
if data == nil {
|
||||
f._bufferData.Invoke(int(target), size, int(usage))
|
||||
} else {
|
||||
if len(data) != size {
|
||||
panic("size mismatch")
|
||||
}
|
||||
f._bufferData.Invoke(int(target), f.byteArrayOf(data), int(usage))
|
||||
}
|
||||
}
|
||||
|
||||
func (f *Functions) BufferSubData(target Enum, offset int, src []byte) {
|
||||
f._bufferSubData.Invoke(int(target), offset, f.byteArrayOf(src))
|
||||
}
|
||||
|
||||
func (f *Functions) CheckFramebufferStatus(target Enum) Enum {
|
||||
status := Enum(f._checkFramebufferStatus.Invoke(int(target)).Int())
|
||||
if status != FRAMEBUFFER_COMPLETE && f.Ctx.Call("isContextLost").Bool() {
|
||||
// If the context is lost, we say that everything is fine. That saves internal/opengl/opengl.go from panic.
|
||||
return FRAMEBUFFER_COMPLETE
|
||||
}
|
||||
return status
|
||||
}
|
||||
|
||||
func (f *Functions) Clear(mask Enum) {
|
||||
f._clear.Invoke(int(mask))
|
||||
}
|
||||
|
||||
func (f *Functions) ClearColor(red, green, blue, alpha float32) {
|
||||
f._clearColor.Invoke(red, green, blue, alpha)
|
||||
}
|
||||
|
||||
func (f *Functions) ClearDepthf(d float32) {
|
||||
f._clearDepth.Invoke(d)
|
||||
}
|
||||
|
||||
func (f *Functions) CompileShader(s Shader) {
|
||||
f._compileShader.Invoke(js.Value(s))
|
||||
}
|
||||
|
||||
func (f *Functions) CopyTexSubImage2D(target Enum, level, xoffset, yoffset, x, y, width, height int) {
|
||||
f._copyTexSubImage2D.Invoke(int(target), level, xoffset, yoffset, x, y, width, height)
|
||||
}
|
||||
|
||||
func (f *Functions) CreateBuffer() Buffer {
|
||||
return Buffer(f._createBuffer.Invoke())
|
||||
}
|
||||
|
||||
func (f *Functions) CreateFramebuffer() Framebuffer {
|
||||
return Framebuffer(f._createFramebuffer.Invoke())
|
||||
}
|
||||
|
||||
func (f *Functions) CreateProgram() Program {
|
||||
return Program(f._createProgram.Invoke())
|
||||
}
|
||||
|
||||
func (f *Functions) CreateQuery() Query {
|
||||
return Query(f._createQuery.Invoke())
|
||||
}
|
||||
|
||||
func (f *Functions) CreateRenderbuffer() Renderbuffer {
|
||||
return Renderbuffer(f._createRenderbuffer.Invoke())
|
||||
}
|
||||
|
||||
func (f *Functions) CreateShader(ty Enum) Shader {
|
||||
return Shader(f._createShader.Invoke(int(ty)))
|
||||
}
|
||||
|
||||
func (f *Functions) CreateTexture() Texture {
|
||||
return Texture(f._createTexture.Invoke())
|
||||
}
|
||||
|
||||
func (f *Functions) CreateVertexArray() VertexArray {
|
||||
panic("not supported")
|
||||
}
|
||||
|
||||
func (f *Functions) DeleteBuffer(v Buffer) {
|
||||
f._deleteBuffer.Invoke(js.Value(v))
|
||||
}
|
||||
|
||||
func (f *Functions) DeleteFramebuffer(v Framebuffer) {
|
||||
f._deleteFramebuffer.Invoke(js.Value(v))
|
||||
}
|
||||
|
||||
func (f *Functions) DeleteProgram(p Program) {
|
||||
f._deleteProgram.Invoke(js.Value(p))
|
||||
}
|
||||
|
||||
func (f *Functions) DeleteQuery(query Query) {
|
||||
if !f.EXT_disjoint_timer_query_webgl2.IsNull() {
|
||||
f._deleteQuery.Invoke(js.Value(query))
|
||||
} else {
|
||||
f.EXT_disjoint_timer_query.Call("deleteQueryEXT", js.Value(query))
|
||||
}
|
||||
}
|
||||
|
||||
func (f *Functions) DeleteShader(s Shader) {
|
||||
f._deleteShader.Invoke(js.Value(s))
|
||||
}
|
||||
|
||||
func (f *Functions) DeleteRenderbuffer(v Renderbuffer) {
|
||||
f._deleteRenderbuffer.Invoke(js.Value(v))
|
||||
}
|
||||
|
||||
func (f *Functions) DeleteTexture(v Texture) {
|
||||
f._deleteTexture.Invoke(js.Value(v))
|
||||
}
|
||||
|
||||
func (f *Functions) DeleteVertexArray(a VertexArray) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (f *Functions) DepthFunc(fn Enum) {
|
||||
f._depthFunc.Invoke(int(fn))
|
||||
}
|
||||
|
||||
func (f *Functions) DepthMask(mask bool) {
|
||||
f._depthMask.Invoke(mask)
|
||||
}
|
||||
|
||||
func (f *Functions) DisableVertexAttribArray(a Attrib) {
|
||||
f._disableVertexAttribArray.Invoke(int(a))
|
||||
}
|
||||
|
||||
func (f *Functions) Disable(cap Enum) {
|
||||
f._disable.Invoke(int(cap))
|
||||
}
|
||||
|
||||
func (f *Functions) DrawArrays(mode Enum, first, count int) {
|
||||
f._drawArrays.Invoke(int(mode), first, count)
|
||||
}
|
||||
|
||||
func (f *Functions) DrawElements(mode Enum, count int, ty Enum, offset int) {
|
||||
f._drawElements.Invoke(int(mode), count, int(ty), offset)
|
||||
}
|
||||
|
||||
func (f *Functions) DispatchCompute(x, y, z int) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (f *Functions) Enable(cap Enum) {
|
||||
f._enable.Invoke(int(cap))
|
||||
}
|
||||
|
||||
func (f *Functions) EnableVertexAttribArray(a Attrib) {
|
||||
f._enableVertexAttribArray.Invoke(int(a))
|
||||
}
|
||||
|
||||
func (f *Functions) EndQuery(target Enum) {
|
||||
if !f.EXT_disjoint_timer_query_webgl2.IsNull() {
|
||||
f._endQuery.Invoke(int(target))
|
||||
} else {
|
||||
f.EXT_disjoint_timer_query.Call("endQueryEXT", int(target))
|
||||
}
|
||||
}
|
||||
|
||||
func (f *Functions) Finish() {
|
||||
f._finish.Invoke()
|
||||
}
|
||||
|
||||
func (f *Functions) Flush() {
|
||||
f._flush.Invoke()
|
||||
}
|
||||
|
||||
func (f *Functions) FramebufferRenderbuffer(target, attachment, renderbuffertarget Enum, renderbuffer Renderbuffer) {
|
||||
f._framebufferRenderbuffer.Invoke(int(target), int(attachment), int(renderbuffertarget), js.Value(renderbuffer))
|
||||
}
|
||||
|
||||
func (f *Functions) FramebufferTexture2D(target, attachment, texTarget Enum, t Texture, level int) {
|
||||
f._framebufferTexture2D.Invoke(int(target), int(attachment), int(texTarget), js.Value(t), level)
|
||||
}
|
||||
|
||||
func (f *Functions) GenerateMipmap(target Enum) {
|
||||
f._generateMipmap.Invoke(int(target))
|
||||
}
|
||||
|
||||
func (f *Functions) GetError() Enum {
|
||||
// Avoid slow getError calls. See gio#179.
|
||||
return 0
|
||||
}
|
||||
|
||||
func (f *Functions) GetRenderbufferParameteri(target, pname Enum) int {
|
||||
return paramVal(f._getRenderbufferParameteri.Invoke(int(pname)))
|
||||
}
|
||||
|
||||
func (f *Functions) GetFramebufferAttachmentParameteri(target, attachment, pname Enum) int {
|
||||
if !f.isWebGL2 && pname == FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING {
|
||||
// FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING is only available on WebGL 2
|
||||
return LINEAR
|
||||
}
|
||||
return paramVal(f._getFramebufferAttachmentParameter.Invoke(int(target), int(attachment), int(pname)))
|
||||
}
|
||||
|
||||
func (f *Functions) GetBinding(pname Enum) Object {
|
||||
obj := f._getParameter.Invoke(int(pname))
|
||||
if !obj.Truthy() {
|
||||
return Object{}
|
||||
}
|
||||
return Object(obj)
|
||||
}
|
||||
|
||||
func (f *Functions) GetBindingi(pname Enum, idx int) Object {
|
||||
obj := f._getIndexedParameter.Invoke(int(pname), idx)
|
||||
if !obj.Truthy() {
|
||||
return Object{}
|
||||
}
|
||||
return Object(obj)
|
||||
}
|
||||
|
||||
func (f *Functions) GetInteger(pname Enum) int {
|
||||
if !f.isWebGL2 {
|
||||
switch pname {
|
||||
case PACK_ROW_LENGTH, UNPACK_ROW_LENGTH:
|
||||
return 0 // PACK_ROW_LENGTH and UNPACK_ROW_LENGTH is only available on WebGL 2
|
||||
}
|
||||
}
|
||||
return paramVal(f._getParameter.Invoke(int(pname)))
|
||||
}
|
||||
|
||||
func (f *Functions) GetFloat(pname Enum) float32 {
|
||||
return float32(f._getParameter.Invoke(int(pname)).Float())
|
||||
}
|
||||
|
||||
func (f *Functions) GetInteger4(pname Enum) [4]int {
|
||||
arr := f._getParameter.Invoke(int(pname))
|
||||
var res [4]int
|
||||
for i := range res {
|
||||
res[i] = arr.Index(i).Int()
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func (f *Functions) GetFloat4(pname Enum) [4]float32 {
|
||||
arr := f._getParameter.Invoke(int(pname))
|
||||
var res [4]float32
|
||||
for i := range res {
|
||||
res[i] = float32(arr.Index(i).Float())
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func (f *Functions) GetProgrami(p Program, pname Enum) int {
|
||||
return paramVal(f._getProgramParameter.Invoke(js.Value(p), int(pname)))
|
||||
}
|
||||
|
||||
func (f *Functions) GetProgramInfoLog(p Program) string {
|
||||
return f._getProgramInfoLog.Invoke(js.Value(p)).String()
|
||||
}
|
||||
|
||||
func (f *Functions) GetQueryObjectuiv(query Query, pname Enum) uint {
|
||||
if !f.EXT_disjoint_timer_query_webgl2.IsNull() {
|
||||
return uint(paramVal(f._getQueryParameter.Invoke(js.Value(query), int(pname))))
|
||||
} else {
|
||||
return uint(paramVal(f.EXT_disjoint_timer_query.Call("getQueryObjectEXT", js.Value(query), int(pname))))
|
||||
}
|
||||
}
|
||||
|
||||
func (f *Functions) GetShaderi(s Shader, pname Enum) int {
|
||||
return paramVal(f._getShaderParameter.Invoke(js.Value(s), int(pname)))
|
||||
}
|
||||
|
||||
func (f *Functions) GetShaderInfoLog(s Shader) string {
|
||||
return f._getShaderInfoLog.Invoke(js.Value(s)).String()
|
||||
}
|
||||
|
||||
func (f *Functions) GetString(pname Enum) string {
|
||||
switch pname {
|
||||
case EXTENSIONS:
|
||||
extsjs := f._getSupportedExtensions.Invoke()
|
||||
var exts []string
|
||||
for i := 0; i < extsjs.Length(); i++ {
|
||||
exts = append(exts, "GL_"+extsjs.Index(i).String())
|
||||
}
|
||||
return strings.Join(exts, " ")
|
||||
default:
|
||||
return f._getParameter.Invoke(int(pname)).String()
|
||||
}
|
||||
}
|
||||
|
||||
func (f *Functions) GetUniformBlockIndex(p Program, name string) uint {
|
||||
return uint(paramVal(f._getUniformBlockIndex.Invoke(js.Value(p), name)))
|
||||
}
|
||||
|
||||
func (f *Functions) GetUniformLocation(p Program, name string) Uniform {
|
||||
return Uniform(f._getUniformLocation.Invoke(js.Value(p), name))
|
||||
}
|
||||
|
||||
func (f *Functions) GetVertexAttrib(index int, pname Enum) int {
|
||||
return paramVal(f._getVertexAttrib.Invoke(index, int(pname)))
|
||||
}
|
||||
|
||||
func (f *Functions) GetVertexAttribBinding(index int, pname Enum) Object {
|
||||
obj := f._getVertexAttrib.Invoke(index, int(pname))
|
||||
if !obj.Truthy() {
|
||||
return Object{}
|
||||
}
|
||||
return Object(obj)
|
||||
}
|
||||
|
||||
func (f *Functions) GetVertexAttribPointer(index int, pname Enum) uintptr {
|
||||
return uintptr(f._getVertexAttribOffset.Invoke(index, int(pname)).Int())
|
||||
}
|
||||
|
||||
func (f *Functions) InvalidateFramebuffer(target, attachment Enum) {
|
||||
fn := f.Ctx.Get("invalidateFramebuffer")
|
||||
if !fn.IsUndefined() {
|
||||
if f.int32Buf.IsUndefined() {
|
||||
f.int32Buf = js.Global().Get("Int32Array").New(1)
|
||||
}
|
||||
f.int32Buf.SetIndex(0, int32(attachment))
|
||||
f._invalidateFramebuffer.Invoke(int(target), f.int32Buf)
|
||||
}
|
||||
}
|
||||
|
||||
func (f *Functions) IsEnabled(cap Enum) bool {
|
||||
return f._isEnabled.Invoke(int(cap)).Truthy()
|
||||
}
|
||||
|
||||
func (f *Functions) LinkProgram(p Program) {
|
||||
f._linkProgram.Invoke(js.Value(p))
|
||||
}
|
||||
|
||||
func (f *Functions) PixelStorei(pname Enum, param int) {
|
||||
f._pixelStorei.Invoke(int(pname), param)
|
||||
}
|
||||
|
||||
func (f *Functions) MemoryBarrier(barriers Enum) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (f *Functions) MapBufferRange(target Enum, offset, length int, access Enum) []byte {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (f *Functions) RenderbufferStorage(target, internalformat Enum, width, height int) {
|
||||
f._renderbufferStorage.Invoke(int(target), int(internalformat), width, height)
|
||||
}
|
||||
|
||||
func (f *Functions) ReadPixels(x, y, width, height int, format, ty Enum, data []byte) {
|
||||
ba := f.byteArrayOf(data)
|
||||
f._readPixels.Invoke(x, y, width, height, int(format), int(ty), ba)
|
||||
js.CopyBytesToGo(data, ba)
|
||||
}
|
||||
|
||||
func (f *Functions) Scissor(x, y, width, height int32) {
|
||||
f._scissor.Invoke(x, y, width, height)
|
||||
}
|
||||
|
||||
func (f *Functions) ShaderSource(s Shader, src string) {
|
||||
f._shaderSource.Invoke(js.Value(s), src)
|
||||
}
|
||||
|
||||
func (f *Functions) TexImage2D(target Enum, level int, internalFormat Enum, width, height int, format, ty Enum) {
|
||||
f._texImage2D.Invoke(int(target), int(level), int(internalFormat), int(width), int(height), 0, int(format), int(ty), nil)
|
||||
}
|
||||
|
||||
func (f *Functions) TexStorage2D(target Enum, levels int, internalFormat Enum, width, height int) {
|
||||
f._texStorage2D.Invoke(int(target), levels, int(internalFormat), width, height)
|
||||
}
|
||||
|
||||
func (f *Functions) TexSubImage2D(target Enum, level int, x, y, width, height int, format, ty Enum, data []byte) {
|
||||
f._texSubImage2D.Invoke(int(target), level, x, y, width, height, int(format), int(ty), f.byteArrayOf(data))
|
||||
}
|
||||
|
||||
func (f *Functions) TexParameteri(target, pname Enum, param int) {
|
||||
f._texParameteri.Invoke(int(target), int(pname), int(param))
|
||||
}
|
||||
|
||||
func (f *Functions) UniformBlockBinding(p Program, uniformBlockIndex uint, uniformBlockBinding uint) {
|
||||
f._uniformBlockBinding.Invoke(js.Value(p), int(uniformBlockIndex), int(uniformBlockBinding))
|
||||
}
|
||||
|
||||
func (f *Functions) Uniform1f(dst Uniform, v float32) {
|
||||
f._uniform1f.Invoke(js.Value(dst), v)
|
||||
}
|
||||
|
||||
func (f *Functions) Uniform1i(dst Uniform, v int) {
|
||||
f._uniform1i.Invoke(js.Value(dst), v)
|
||||
}
|
||||
|
||||
func (f *Functions) Uniform2f(dst Uniform, v0, v1 float32) {
|
||||
f._uniform2f.Invoke(js.Value(dst), v0, v1)
|
||||
}
|
||||
|
||||
func (f *Functions) Uniform3f(dst Uniform, v0, v1, v2 float32) {
|
||||
f._uniform3f.Invoke(js.Value(dst), v0, v1, v2)
|
||||
}
|
||||
|
||||
func (f *Functions) Uniform4f(dst Uniform, v0, v1, v2, v3 float32) {
|
||||
f._uniform4f.Invoke(js.Value(dst), v0, v1, v2, v3)
|
||||
}
|
||||
|
||||
func (f *Functions) UseProgram(p Program) {
|
||||
f._useProgram.Invoke(js.Value(p))
|
||||
}
|
||||
|
||||
func (f *Functions) UnmapBuffer(target Enum) bool {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (f *Functions) VertexAttribPointer(dst Attrib, size int, ty Enum, normalized bool, stride, offset int) {
|
||||
f._vertexAttribPointer.Invoke(int(dst), size, int(ty), normalized, stride, offset)
|
||||
}
|
||||
|
||||
func (f *Functions) Viewport(x, y, width, height int) {
|
||||
f._viewport.Invoke(x, y, width, height)
|
||||
}
|
||||
|
||||
func (f *Functions) byteArrayOf(data []byte) js.Value {
|
||||
if len(data) == 0 {
|
||||
return js.Null()
|
||||
}
|
||||
f.resizeByteBuffer(len(data))
|
||||
ba := f.uint8Array.New(f.arrayBuf, int(0), int(len(data)))
|
||||
js.CopyBytesToJS(ba, data)
|
||||
return ba
|
||||
}
|
||||
|
||||
func (f *Functions) resizeByteBuffer(n int) {
|
||||
if n == 0 {
|
||||
return
|
||||
}
|
||||
if !f.arrayBuf.IsUndefined() && f.arrayBuf.Length() >= n {
|
||||
return
|
||||
}
|
||||
f.arrayBuf = js.Global().Get("ArrayBuffer").New(n)
|
||||
}
|
||||
|
||||
func paramVal(v js.Value) int {
|
||||
switch v.Type() {
|
||||
case js.TypeBoolean:
|
||||
if b := v.Bool(); b {
|
||||
return 1
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
case js.TypeNumber:
|
||||
return v.Int()
|
||||
case js.TypeUndefined:
|
||||
return 0
|
||||
case js.TypeNull:
|
||||
return 0
|
||||
default:
|
||||
panic("unknown parameter type")
|
||||
}
|
||||
}
|
||||
+1323
File diff suppressed because it is too large
Load Diff
+721
@@ -0,0 +1,721 @@
|
||||
// SPDX-License-Identifier: Unlicense OR MIT
|
||||
|
||||
package gl
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"runtime"
|
||||
"sync"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
func loadGLESv2Procs() error {
|
||||
dllName := "libGLESv2.dll"
|
||||
handle, err := windows.LoadLibraryEx(dllName, 0, windows.LOAD_LIBRARY_SEARCH_DEFAULT_DIRS)
|
||||
if err != nil {
|
||||
return fmt.Errorf("gl: failed to load %s: %v", dllName, err)
|
||||
}
|
||||
gles := windows.DLL{Handle: handle, Name: dllName}
|
||||
// d3dcompiler_47.dll is needed internally for shader compilation to function.
|
||||
dllName = "d3dcompiler_47.dll"
|
||||
_, err = windows.LoadLibraryEx(dllName, 0, windows.LOAD_LIBRARY_SEARCH_DEFAULT_DIRS)
|
||||
if err != nil {
|
||||
return fmt.Errorf("gl: failed to load %s: %v", dllName, err)
|
||||
}
|
||||
procs := map[string]**windows.Proc{
|
||||
"glActiveTexture": &_glActiveTexture,
|
||||
"glAttachShader": &_glAttachShader,
|
||||
"glBeginQuery": &_glBeginQuery,
|
||||
"glBindAttribLocation": &_glBindAttribLocation,
|
||||
"glBindBuffer": &_glBindBuffer,
|
||||
"glBindBufferBase": &_glBindBufferBase,
|
||||
"glBindFramebuffer": &_glBindFramebuffer,
|
||||
"glBindRenderbuffer": &_glBindRenderbuffer,
|
||||
"glBindTexture": &_glBindTexture,
|
||||
"glBindVertexArray": &_glBindVertexArray,
|
||||
"glBlendEquation": &_glBlendEquation,
|
||||
"glBlendFuncSeparate": &_glBlendFuncSeparate,
|
||||
"glBufferData": &_glBufferData,
|
||||
"glBufferSubData": &_glBufferSubData,
|
||||
"glCheckFramebufferStatus": &_glCheckFramebufferStatus,
|
||||
"glClear": &_glClear,
|
||||
"glClearColor": &_glClearColor,
|
||||
"glClearDepthf": &_glClearDepthf,
|
||||
"glDeleteQueries": &_glDeleteQueries,
|
||||
"glDeleteVertexArrays": &_glDeleteVertexArrays,
|
||||
"glCompileShader": &_glCompileShader,
|
||||
"glCopyTexSubImage2D": &_glCopyTexSubImage2D,
|
||||
"glGenerateMipmap": &_glGenerateMipmap,
|
||||
"glGenBuffers": &_glGenBuffers,
|
||||
"glGenFramebuffers": &_glGenFramebuffers,
|
||||
"glGenVertexArrays": &_glGenVertexArrays,
|
||||
"glGetUniformBlockIndex": &_glGetUniformBlockIndex,
|
||||
"glCreateProgram": &_glCreateProgram,
|
||||
"glGenRenderbuffers": &_glGenRenderbuffers,
|
||||
"glCreateShader": &_glCreateShader,
|
||||
"glGenTextures": &_glGenTextures,
|
||||
"glDeleteBuffers": &_glDeleteBuffers,
|
||||
"glDeleteFramebuffers": &_glDeleteFramebuffers,
|
||||
"glDeleteProgram": &_glDeleteProgram,
|
||||
"glDeleteShader": &_glDeleteShader,
|
||||
"glDeleteRenderbuffers": &_glDeleteRenderbuffers,
|
||||
"glDeleteTextures": &_glDeleteTextures,
|
||||
"glDepthFunc": &_glDepthFunc,
|
||||
"glDepthMask": &_glDepthMask,
|
||||
"glDisableVertexAttribArray": &_glDisableVertexAttribArray,
|
||||
"glDisable": &_glDisable,
|
||||
"glDrawArrays": &_glDrawArrays,
|
||||
"glDrawElements": &_glDrawElements,
|
||||
"glEnable": &_glEnable,
|
||||
"glEnableVertexAttribArray": &_glEnableVertexAttribArray,
|
||||
"glEndQuery": &_glEndQuery,
|
||||
"glFinish": &_glFinish,
|
||||
"glFlush": &_glFlush,
|
||||
"glFramebufferRenderbuffer": &_glFramebufferRenderbuffer,
|
||||
"glFramebufferTexture2D": &_glFramebufferTexture2D,
|
||||
"glGenQueries": &_glGenQueries,
|
||||
"glGetError": &_glGetError,
|
||||
"glGetRenderbufferParameteriv": &_glGetRenderbufferParameteriv,
|
||||
"glGetFloatv": &_glGetFloatv,
|
||||
"glGetFramebufferAttachmentParameteriv": &_glGetFramebufferAttachmentParameteriv,
|
||||
"glGetIntegerv": &_glGetIntegerv,
|
||||
"glGetIntegeri_v": &_glGetIntegeri_v,
|
||||
"glGetProgramiv": &_glGetProgramiv,
|
||||
"glGetProgramInfoLog": &_glGetProgramInfoLog,
|
||||
"glGetQueryObjectuiv": &_glGetQueryObjectuiv,
|
||||
"glGetShaderiv": &_glGetShaderiv,
|
||||
"glGetShaderInfoLog": &_glGetShaderInfoLog,
|
||||
"glGetString": &_glGetString,
|
||||
"glGetUniformLocation": &_glGetUniformLocation,
|
||||
"glGetVertexAttribiv": &_glGetVertexAttribiv,
|
||||
"glGetVertexAttribPointerv": &_glGetVertexAttribPointerv,
|
||||
"glInvalidateFramebuffer": &_glInvalidateFramebuffer,
|
||||
"glIsEnabled": &_glIsEnabled,
|
||||
"glLinkProgram": &_glLinkProgram,
|
||||
"glPixelStorei": &_glPixelStorei,
|
||||
"glReadPixels": &_glReadPixels,
|
||||
"glRenderbufferStorage": &_glRenderbufferStorage,
|
||||
"glScissor": &_glScissor,
|
||||
"glShaderSource": &_glShaderSource,
|
||||
"glTexImage2D": &_glTexImage2D,
|
||||
"glTexStorage2D": &_glTexStorage2D,
|
||||
"glTexSubImage2D": &_glTexSubImage2D,
|
||||
"glTexParameteri": &_glTexParameteri,
|
||||
"glUniformBlockBinding": &_glUniformBlockBinding,
|
||||
"glUniform1f": &_glUniform1f,
|
||||
"glUniform1i": &_glUniform1i,
|
||||
"glUniform2f": &_glUniform2f,
|
||||
"glUniform3f": &_glUniform3f,
|
||||
"glUniform4f": &_glUniform4f,
|
||||
"glUseProgram": &_glUseProgram,
|
||||
"glVertexAttribPointer": &_glVertexAttribPointer,
|
||||
"glViewport": &_glViewport,
|
||||
}
|
||||
for name, proc := range procs {
|
||||
p, err := gles.FindProc(name)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to locate %s in %s: %w", name, gles.Name, err)
|
||||
}
|
||||
*proc = p
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
glInitOnce sync.Once
|
||||
_glActiveTexture *windows.Proc
|
||||
_glAttachShader *windows.Proc
|
||||
_glBeginQuery *windows.Proc
|
||||
_glBindAttribLocation *windows.Proc
|
||||
_glBindBuffer *windows.Proc
|
||||
_glBindBufferBase *windows.Proc
|
||||
_glBindFramebuffer *windows.Proc
|
||||
_glBindRenderbuffer *windows.Proc
|
||||
_glBindTexture *windows.Proc
|
||||
_glBindVertexArray *windows.Proc
|
||||
_glBlendEquation *windows.Proc
|
||||
_glBlendFuncSeparate *windows.Proc
|
||||
_glBufferData *windows.Proc
|
||||
_glBufferSubData *windows.Proc
|
||||
_glCheckFramebufferStatus *windows.Proc
|
||||
_glClear *windows.Proc
|
||||
_glClearColor *windows.Proc
|
||||
_glClearDepthf *windows.Proc
|
||||
_glDeleteQueries *windows.Proc
|
||||
_glDeleteVertexArrays *windows.Proc
|
||||
_glCompileShader *windows.Proc
|
||||
_glCopyTexSubImage2D *windows.Proc
|
||||
_glGenerateMipmap *windows.Proc
|
||||
_glGenBuffers *windows.Proc
|
||||
_glGenFramebuffers *windows.Proc
|
||||
_glGenVertexArrays *windows.Proc
|
||||
_glGetUniformBlockIndex *windows.Proc
|
||||
_glCreateProgram *windows.Proc
|
||||
_glGenRenderbuffers *windows.Proc
|
||||
_glCreateShader *windows.Proc
|
||||
_glGenTextures *windows.Proc
|
||||
_glDeleteBuffers *windows.Proc
|
||||
_glDeleteFramebuffers *windows.Proc
|
||||
_glDeleteProgram *windows.Proc
|
||||
_glDeleteShader *windows.Proc
|
||||
_glDeleteRenderbuffers *windows.Proc
|
||||
_glDeleteTextures *windows.Proc
|
||||
_glDepthFunc *windows.Proc
|
||||
_glDepthMask *windows.Proc
|
||||
_glDisableVertexAttribArray *windows.Proc
|
||||
_glDisable *windows.Proc
|
||||
_glDrawArrays *windows.Proc
|
||||
_glDrawElements *windows.Proc
|
||||
_glEnable *windows.Proc
|
||||
_glEnableVertexAttribArray *windows.Proc
|
||||
_glEndQuery *windows.Proc
|
||||
_glFinish *windows.Proc
|
||||
_glFlush *windows.Proc
|
||||
_glFramebufferRenderbuffer *windows.Proc
|
||||
_glFramebufferTexture2D *windows.Proc
|
||||
_glGenQueries *windows.Proc
|
||||
_glGetError *windows.Proc
|
||||
_glGetRenderbufferParameteriv *windows.Proc
|
||||
_glGetFloatv *windows.Proc
|
||||
_glGetFramebufferAttachmentParameteriv *windows.Proc
|
||||
_glGetIntegerv *windows.Proc
|
||||
_glGetIntegeri_v *windows.Proc
|
||||
_glGetProgramiv *windows.Proc
|
||||
_glGetProgramInfoLog *windows.Proc
|
||||
_glGetQueryObjectuiv *windows.Proc
|
||||
_glGetShaderiv *windows.Proc
|
||||
_glGetShaderInfoLog *windows.Proc
|
||||
_glGetString *windows.Proc
|
||||
_glGetUniformLocation *windows.Proc
|
||||
_glGetVertexAttribiv *windows.Proc
|
||||
_glGetVertexAttribPointerv *windows.Proc
|
||||
_glInvalidateFramebuffer *windows.Proc
|
||||
_glIsEnabled *windows.Proc
|
||||
_glLinkProgram *windows.Proc
|
||||
_glPixelStorei *windows.Proc
|
||||
_glReadPixels *windows.Proc
|
||||
_glRenderbufferStorage *windows.Proc
|
||||
_glScissor *windows.Proc
|
||||
_glShaderSource *windows.Proc
|
||||
_glTexImage2D *windows.Proc
|
||||
_glTexStorage2D *windows.Proc
|
||||
_glTexSubImage2D *windows.Proc
|
||||
_glTexParameteri *windows.Proc
|
||||
_glUniformBlockBinding *windows.Proc
|
||||
_glUniform1f *windows.Proc
|
||||
_glUniform1i *windows.Proc
|
||||
_glUniform2f *windows.Proc
|
||||
_glUniform3f *windows.Proc
|
||||
_glUniform4f *windows.Proc
|
||||
_glUseProgram *windows.Proc
|
||||
_glVertexAttribPointer *windows.Proc
|
||||
_glViewport *windows.Proc
|
||||
)
|
||||
|
||||
type Functions struct {
|
||||
// Query caches.
|
||||
int32s [100]int32
|
||||
float32s [100]float32
|
||||
uintptrs [100]uintptr
|
||||
}
|
||||
|
||||
type Context any
|
||||
|
||||
func NewFunctions(ctx Context, forceES bool) (*Functions, error) {
|
||||
if ctx != nil {
|
||||
panic("non-nil context")
|
||||
}
|
||||
var err error
|
||||
glInitOnce.Do(func() {
|
||||
err = loadGLESv2Procs()
|
||||
})
|
||||
return new(Functions), err
|
||||
}
|
||||
|
||||
func (c *Functions) ActiveTexture(t Enum) {
|
||||
syscall.Syscall(_glActiveTexture.Addr(), 1, uintptr(t), 0, 0)
|
||||
}
|
||||
|
||||
func (c *Functions) AttachShader(p Program, s Shader) {
|
||||
syscall.Syscall(_glAttachShader.Addr(), 2, uintptr(p.V), uintptr(s.V), 0)
|
||||
}
|
||||
|
||||
func (f *Functions) BeginQuery(target Enum, query Query) {
|
||||
syscall.Syscall(_glBeginQuery.Addr(), 2, uintptr(target), uintptr(query.V), 0)
|
||||
}
|
||||
|
||||
func (c *Functions) BindAttribLocation(p Program, a Attrib, name string) {
|
||||
cname := cString(name)
|
||||
c0 := &cname[0]
|
||||
syscall.Syscall(_glBindAttribLocation.Addr(), 3, uintptr(p.V), uintptr(a), uintptr(unsafe.Pointer(c0)))
|
||||
issue34474KeepAlive(c)
|
||||
}
|
||||
|
||||
func (c *Functions) BindBuffer(target Enum, b Buffer) {
|
||||
syscall.Syscall(_glBindBuffer.Addr(), 2, uintptr(target), uintptr(b.V), 0)
|
||||
}
|
||||
|
||||
func (c *Functions) BindBufferBase(target Enum, index int, b Buffer) {
|
||||
syscall.Syscall(_glBindBufferBase.Addr(), 3, uintptr(target), uintptr(index), uintptr(b.V))
|
||||
}
|
||||
|
||||
func (c *Functions) BindFramebuffer(target Enum, fb Framebuffer) {
|
||||
syscall.Syscall(_glBindFramebuffer.Addr(), 2, uintptr(target), uintptr(fb.V), 0)
|
||||
}
|
||||
|
||||
func (c *Functions) BindRenderbuffer(target Enum, rb Renderbuffer) {
|
||||
syscall.Syscall(_glBindRenderbuffer.Addr(), 2, uintptr(target), uintptr(rb.V), 0)
|
||||
}
|
||||
|
||||
func (f *Functions) BindImageTexture(unit int, t Texture, level int, layered bool, layer int, access, format Enum) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (c *Functions) BindTexture(target Enum, t Texture) {
|
||||
syscall.Syscall(_glBindTexture.Addr(), 2, uintptr(target), uintptr(t.V), 0)
|
||||
}
|
||||
|
||||
func (c *Functions) BindVertexArray(a VertexArray) {
|
||||
syscall.Syscall(_glBindVertexArray.Addr(), 1, uintptr(a.V), 0, 0)
|
||||
}
|
||||
|
||||
func (c *Functions) BlendEquation(mode Enum) {
|
||||
syscall.Syscall(_glBlendEquation.Addr(), 1, uintptr(mode), 0, 0)
|
||||
}
|
||||
|
||||
func (c *Functions) BlendFuncSeparate(srcRGB, dstRGB, srcA, dstA Enum) {
|
||||
syscall.Syscall6(_glBlendFuncSeparate.Addr(), 4, uintptr(srcRGB), uintptr(dstRGB), uintptr(srcA), uintptr(dstA), 0, 0)
|
||||
}
|
||||
|
||||
func (c *Functions) BufferData(target Enum, size int, usage Enum, data []byte) {
|
||||
var p unsafe.Pointer
|
||||
if len(data) > 0 {
|
||||
p = unsafe.Pointer(&data[0])
|
||||
}
|
||||
syscall.Syscall6(_glBufferData.Addr(), 4, uintptr(target), uintptr(size), uintptr(p), uintptr(usage), 0, 0)
|
||||
}
|
||||
|
||||
func (f *Functions) BufferSubData(target Enum, offset int, src []byte) {
|
||||
if n := len(src); n > 0 {
|
||||
s0 := &src[0]
|
||||
syscall.Syscall6(_glBufferSubData.Addr(), 4, uintptr(target), uintptr(offset), uintptr(n), uintptr(unsafe.Pointer(s0)), 0, 0)
|
||||
issue34474KeepAlive(s0)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Functions) CheckFramebufferStatus(target Enum) Enum {
|
||||
s, _, _ := syscall.Syscall(_glCheckFramebufferStatus.Addr(), 1, uintptr(target), 0, 0)
|
||||
return Enum(s)
|
||||
}
|
||||
|
||||
func (c *Functions) Clear(mask Enum) {
|
||||
syscall.Syscall(_glClear.Addr(), 1, uintptr(mask), 0, 0)
|
||||
}
|
||||
|
||||
func (c *Functions) ClearColor(red, green, blue, alpha float32) {
|
||||
syscall.Syscall6(_glClearColor.Addr(), 4, uintptr(math.Float32bits(red)), uintptr(math.Float32bits(green)), uintptr(math.Float32bits(blue)), uintptr(math.Float32bits(alpha)), 0, 0)
|
||||
}
|
||||
|
||||
func (c *Functions) ClearDepthf(d float32) {
|
||||
syscall.Syscall(_glClearDepthf.Addr(), 1, uintptr(math.Float32bits(d)), 0, 0)
|
||||
}
|
||||
|
||||
func (c *Functions) CompileShader(s Shader) {
|
||||
syscall.Syscall(_glCompileShader.Addr(), 1, uintptr(s.V), 0, 0)
|
||||
}
|
||||
|
||||
func (f *Functions) CopyTexSubImage2D(target Enum, level, xoffset, yoffset, x, y, width, height int) {
|
||||
syscall.Syscall9(_glCopyTexSubImage2D.Addr(), 8, uintptr(target), uintptr(level), uintptr(xoffset), uintptr(yoffset), uintptr(x), uintptr(y), uintptr(width), uintptr(height), 0)
|
||||
}
|
||||
|
||||
func (f *Functions) GenerateMipmap(target Enum) {
|
||||
syscall.Syscall(_glGenerateMipmap.Addr(), 1, uintptr(target), 0, 0)
|
||||
}
|
||||
|
||||
func (c *Functions) CreateBuffer() Buffer {
|
||||
var buf uintptr
|
||||
syscall.Syscall(_glGenBuffers.Addr(), 2, 1, uintptr(unsafe.Pointer(&buf)), 0)
|
||||
return Buffer{uint(buf)}
|
||||
}
|
||||
|
||||
func (c *Functions) CreateFramebuffer() Framebuffer {
|
||||
var fb uintptr
|
||||
syscall.Syscall(_glGenFramebuffers.Addr(), 2, 1, uintptr(unsafe.Pointer(&fb)), 0)
|
||||
return Framebuffer{uint(fb)}
|
||||
}
|
||||
|
||||
func (c *Functions) CreateProgram() Program {
|
||||
p, _, _ := syscall.Syscall(_glCreateProgram.Addr(), 0, 0, 0, 0)
|
||||
return Program{uint(p)}
|
||||
}
|
||||
|
||||
func (f *Functions) CreateQuery() Query {
|
||||
var q uintptr
|
||||
syscall.Syscall(_glGenQueries.Addr(), 2, 1, uintptr(unsafe.Pointer(&q)), 0)
|
||||
return Query{uint(q)}
|
||||
}
|
||||
|
||||
func (c *Functions) CreateRenderbuffer() Renderbuffer {
|
||||
var rb uintptr
|
||||
syscall.Syscall(_glGenRenderbuffers.Addr(), 2, 1, uintptr(unsafe.Pointer(&rb)), 0)
|
||||
return Renderbuffer{uint(rb)}
|
||||
}
|
||||
|
||||
func (c *Functions) CreateShader(ty Enum) Shader {
|
||||
s, _, _ := syscall.Syscall(_glCreateShader.Addr(), 1, uintptr(ty), 0, 0)
|
||||
return Shader{uint(s)}
|
||||
}
|
||||
|
||||
func (c *Functions) CreateTexture() Texture {
|
||||
var t uintptr
|
||||
syscall.Syscall(_glGenTextures.Addr(), 2, 1, uintptr(unsafe.Pointer(&t)), 0)
|
||||
return Texture{uint(t)}
|
||||
}
|
||||
|
||||
func (c *Functions) CreateVertexArray() VertexArray {
|
||||
var t uintptr
|
||||
syscall.Syscall(_glGenVertexArrays.Addr(), 2, 1, uintptr(unsafe.Pointer(&t)), 0)
|
||||
return VertexArray{uint(t)}
|
||||
}
|
||||
|
||||
func (c *Functions) DeleteBuffer(v Buffer) {
|
||||
syscall.Syscall(_glDeleteBuffers.Addr(), 2, 1, uintptr(unsafe.Pointer(&v)), 0)
|
||||
}
|
||||
|
||||
func (c *Functions) DeleteFramebuffer(v Framebuffer) {
|
||||
syscall.Syscall(_glDeleteFramebuffers.Addr(), 2, 1, uintptr(unsafe.Pointer(&v.V)), 0)
|
||||
}
|
||||
|
||||
func (c *Functions) DeleteProgram(p Program) {
|
||||
syscall.Syscall(_glDeleteProgram.Addr(), 1, uintptr(p.V), 0, 0)
|
||||
}
|
||||
|
||||
func (f *Functions) DeleteQuery(query Query) {
|
||||
syscall.Syscall(_glDeleteQueries.Addr(), 2, 1, uintptr(unsafe.Pointer(&query.V)), 0)
|
||||
}
|
||||
|
||||
func (c *Functions) DeleteShader(s Shader) {
|
||||
syscall.Syscall(_glDeleteShader.Addr(), 1, uintptr(s.V), 0, 0)
|
||||
}
|
||||
|
||||
func (c *Functions) DeleteRenderbuffer(v Renderbuffer) {
|
||||
syscall.Syscall(_glDeleteRenderbuffers.Addr(), 2, 1, uintptr(unsafe.Pointer(&v.V)), 0)
|
||||
}
|
||||
|
||||
func (c *Functions) DeleteTexture(v Texture) {
|
||||
syscall.Syscall(_glDeleteTextures.Addr(), 2, 1, uintptr(unsafe.Pointer(&v.V)), 0)
|
||||
}
|
||||
|
||||
func (f *Functions) DeleteVertexArray(array VertexArray) {
|
||||
syscall.Syscall(_glDeleteVertexArrays.Addr(), 2, 1, uintptr(unsafe.Pointer(&array.V)), 0)
|
||||
}
|
||||
|
||||
func (c *Functions) DepthFunc(f Enum) {
|
||||
syscall.Syscall(_glDepthFunc.Addr(), 1, uintptr(f), 0, 0)
|
||||
}
|
||||
|
||||
func (c *Functions) DepthMask(mask bool) {
|
||||
var m uintptr
|
||||
if mask {
|
||||
m = 1
|
||||
}
|
||||
syscall.Syscall(_glDepthMask.Addr(), 1, m, 0, 0)
|
||||
}
|
||||
|
||||
func (c *Functions) DisableVertexAttribArray(a Attrib) {
|
||||
syscall.Syscall(_glDisableVertexAttribArray.Addr(), 1, uintptr(a), 0, 0)
|
||||
}
|
||||
|
||||
func (c *Functions) Disable(cap Enum) {
|
||||
syscall.Syscall(_glDisable.Addr(), 1, uintptr(cap), 0, 0)
|
||||
}
|
||||
|
||||
func (c *Functions) DrawArrays(mode Enum, first, count int) {
|
||||
syscall.Syscall(_glDrawArrays.Addr(), 3, uintptr(mode), uintptr(first), uintptr(count))
|
||||
}
|
||||
|
||||
func (c *Functions) DrawElements(mode Enum, count int, ty Enum, offset int) {
|
||||
syscall.Syscall6(_glDrawElements.Addr(), 4, uintptr(mode), uintptr(count), uintptr(ty), uintptr(offset), 0, 0)
|
||||
}
|
||||
|
||||
func (f *Functions) DispatchCompute(x, y, z int) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (c *Functions) Enable(cap Enum) {
|
||||
syscall.Syscall(_glEnable.Addr(), 1, uintptr(cap), 0, 0)
|
||||
}
|
||||
|
||||
func (c *Functions) EnableVertexAttribArray(a Attrib) {
|
||||
syscall.Syscall(_glEnableVertexAttribArray.Addr(), 1, uintptr(a), 0, 0)
|
||||
}
|
||||
|
||||
func (f *Functions) EndQuery(target Enum) {
|
||||
syscall.Syscall(_glEndQuery.Addr(), 1, uintptr(target), 0, 0)
|
||||
}
|
||||
|
||||
func (c *Functions) Finish() {
|
||||
syscall.Syscall(_glFinish.Addr(), 0, 0, 0, 0)
|
||||
}
|
||||
|
||||
func (c *Functions) Flush() {
|
||||
syscall.Syscall(_glFlush.Addr(), 0, 0, 0, 0)
|
||||
}
|
||||
|
||||
func (c *Functions) FramebufferRenderbuffer(target, attachment, renderbuffertarget Enum, renderbuffer Renderbuffer) {
|
||||
syscall.Syscall6(_glFramebufferRenderbuffer.Addr(), 4, uintptr(target), uintptr(attachment), uintptr(renderbuffertarget), uintptr(renderbuffer.V), 0, 0)
|
||||
}
|
||||
|
||||
func (c *Functions) FramebufferTexture2D(target, attachment, texTarget Enum, t Texture, level int) {
|
||||
syscall.Syscall6(_glFramebufferTexture2D.Addr(), 5, uintptr(target), uintptr(attachment), uintptr(texTarget), uintptr(t.V), uintptr(level), 0)
|
||||
}
|
||||
|
||||
func (f *Functions) GetUniformBlockIndex(p Program, name string) uint {
|
||||
cname := cString(name)
|
||||
c0 := &cname[0]
|
||||
u, _, _ := syscall.Syscall(_glGetUniformBlockIndex.Addr(), 2, uintptr(p.V), uintptr(unsafe.Pointer(c0)), 0)
|
||||
issue34474KeepAlive(c0)
|
||||
return uint(u)
|
||||
}
|
||||
|
||||
func (c *Functions) GetBinding(pname Enum) Object {
|
||||
return Object{uint(c.GetInteger(pname))}
|
||||
}
|
||||
|
||||
func (c *Functions) GetBindingi(pname Enum, idx int) Object {
|
||||
return Object{uint(c.GetIntegeri(pname, idx))}
|
||||
}
|
||||
|
||||
func (c *Functions) GetError() Enum {
|
||||
e, _, _ := syscall.Syscall(_glGetError.Addr(), 0, 0, 0, 0)
|
||||
return Enum(e)
|
||||
}
|
||||
|
||||
func (c *Functions) GetRenderbufferParameteri(target, pname Enum) int {
|
||||
syscall.Syscall(_glGetRenderbufferParameteriv.Addr(), 3, uintptr(target), uintptr(pname), uintptr(unsafe.Pointer(&c.int32s[0])))
|
||||
return int(c.int32s[0])
|
||||
}
|
||||
|
||||
func (c *Functions) GetFramebufferAttachmentParameteri(target, attachment, pname Enum) int {
|
||||
syscall.Syscall6(_glGetFramebufferAttachmentParameteriv.Addr(), 4, uintptr(target), uintptr(attachment), uintptr(pname), uintptr(unsafe.Pointer(&c.int32s[0])), 0, 0)
|
||||
return int(c.int32s[0])
|
||||
}
|
||||
|
||||
func (c *Functions) GetInteger4(pname Enum) [4]int {
|
||||
syscall.Syscall(_glGetIntegerv.Addr(), 2, uintptr(pname), uintptr(unsafe.Pointer(&c.int32s[0])), 0)
|
||||
var r [4]int
|
||||
for i := range r {
|
||||
r[i] = int(c.int32s[i])
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func (c *Functions) GetInteger(pname Enum) int {
|
||||
syscall.Syscall(_glGetIntegerv.Addr(), 2, uintptr(pname), uintptr(unsafe.Pointer(&c.int32s[0])), 0)
|
||||
return int(c.int32s[0])
|
||||
}
|
||||
|
||||
func (c *Functions) GetIntegeri(pname Enum, idx int) int {
|
||||
syscall.Syscall(_glGetIntegeri_v.Addr(), 3, uintptr(pname), uintptr(idx), uintptr(unsafe.Pointer(&c.int32s[0])))
|
||||
return int(c.int32s[0])
|
||||
}
|
||||
|
||||
func (c *Functions) GetFloat(pname Enum) float32 {
|
||||
syscall.Syscall(_glGetFloatv.Addr(), 2, uintptr(pname), uintptr(unsafe.Pointer(&c.float32s[0])), 0)
|
||||
return c.float32s[0]
|
||||
}
|
||||
|
||||
func (c *Functions) GetFloat4(pname Enum) [4]float32 {
|
||||
syscall.Syscall(_glGetFloatv.Addr(), 2, uintptr(pname), uintptr(unsafe.Pointer(&c.float32s[0])), 0)
|
||||
var r [4]float32
|
||||
copy(r[:], c.float32s[:])
|
||||
return r
|
||||
}
|
||||
|
||||
func (c *Functions) GetProgrami(p Program, pname Enum) int {
|
||||
syscall.Syscall(_glGetProgramiv.Addr(), 3, uintptr(p.V), uintptr(pname), uintptr(unsafe.Pointer(&c.int32s[0])))
|
||||
return int(c.int32s[0])
|
||||
}
|
||||
|
||||
func (c *Functions) GetProgramInfoLog(p Program) string {
|
||||
n := c.GetProgrami(p, INFO_LOG_LENGTH)
|
||||
if n == 0 {
|
||||
return ""
|
||||
}
|
||||
buf := make([]byte, n)
|
||||
syscall.Syscall6(_glGetProgramInfoLog.Addr(), 4, uintptr(p.V), uintptr(len(buf)), 0, uintptr(unsafe.Pointer(&buf[0])), 0, 0)
|
||||
return string(buf)
|
||||
}
|
||||
|
||||
func (c *Functions) GetQueryObjectuiv(query Query, pname Enum) uint {
|
||||
syscall.Syscall(_glGetQueryObjectuiv.Addr(), 3, uintptr(query.V), uintptr(pname), uintptr(unsafe.Pointer(&c.int32s[0])))
|
||||
return uint(c.int32s[0])
|
||||
}
|
||||
|
||||
func (c *Functions) GetShaderi(s Shader, pname Enum) int {
|
||||
syscall.Syscall(_glGetShaderiv.Addr(), 3, uintptr(s.V), uintptr(pname), uintptr(unsafe.Pointer(&c.int32s[0])))
|
||||
return int(c.int32s[0])
|
||||
}
|
||||
|
||||
func (c *Functions) GetShaderInfoLog(s Shader) string {
|
||||
n := c.GetShaderi(s, INFO_LOG_LENGTH)
|
||||
buf := make([]byte, n)
|
||||
syscall.Syscall6(_glGetShaderInfoLog.Addr(), 4, uintptr(s.V), uintptr(len(buf)), 0, uintptr(unsafe.Pointer(&buf[0])), 0, 0)
|
||||
return string(buf)
|
||||
}
|
||||
|
||||
func (c *Functions) GetString(pname Enum) string {
|
||||
s, _, _ := syscall.Syscall(_glGetString.Addr(), 1, uintptr(pname), 0, 0)
|
||||
return windows.BytePtrToString((*byte)(unsafe.Pointer(s)))
|
||||
}
|
||||
|
||||
func (c *Functions) GetUniformLocation(p Program, name string) Uniform {
|
||||
cname := cString(name)
|
||||
c0 := &cname[0]
|
||||
u, _, _ := syscall.Syscall(_glGetUniformLocation.Addr(), 2, uintptr(p.V), uintptr(unsafe.Pointer(c0)), 0)
|
||||
issue34474KeepAlive(c0)
|
||||
return Uniform{int(u)}
|
||||
}
|
||||
|
||||
func (c *Functions) GetVertexAttrib(index int, pname Enum) int {
|
||||
syscall.Syscall(_glGetVertexAttribiv.Addr(), 3, uintptr(index), uintptr(pname), uintptr(unsafe.Pointer(&c.int32s[0])))
|
||||
return int(c.int32s[0])
|
||||
}
|
||||
|
||||
func (c *Functions) GetVertexAttribBinding(index int, pname Enum) Object {
|
||||
return Object{uint(c.GetVertexAttrib(index, pname))}
|
||||
}
|
||||
|
||||
func (c *Functions) GetVertexAttribPointer(index int, pname Enum) uintptr {
|
||||
syscall.Syscall(_glGetVertexAttribPointerv.Addr(), 3, uintptr(index), uintptr(pname), uintptr(unsafe.Pointer(&c.uintptrs[0])))
|
||||
return c.uintptrs[0]
|
||||
}
|
||||
|
||||
func (c *Functions) InvalidateFramebuffer(target, attachment Enum) {
|
||||
addr := _glInvalidateFramebuffer.Addr()
|
||||
if addr == 0 {
|
||||
// InvalidateFramebuffer is just a hint. Skip it if not supported.
|
||||
return
|
||||
}
|
||||
syscall.Syscall(addr, 3, uintptr(target), 1, uintptr(unsafe.Pointer(&attachment)))
|
||||
}
|
||||
|
||||
func (f *Functions) IsEnabled(cap Enum) bool {
|
||||
u, _, _ := syscall.Syscall(_glIsEnabled.Addr(), 1, uintptr(cap), 0, 0)
|
||||
return u == TRUE
|
||||
}
|
||||
|
||||
func (c *Functions) LinkProgram(p Program) {
|
||||
syscall.Syscall(_glLinkProgram.Addr(), 1, uintptr(p.V), 0, 0)
|
||||
}
|
||||
|
||||
func (c *Functions) PixelStorei(pname Enum, param int) {
|
||||
syscall.Syscall(_glPixelStorei.Addr(), 2, uintptr(pname), uintptr(param), 0)
|
||||
}
|
||||
|
||||
func (f *Functions) MemoryBarrier(barriers Enum) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (f *Functions) MapBufferRange(target Enum, offset, length int, access Enum) []byte {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (f *Functions) ReadPixels(x, y, width, height int, format, ty Enum, data []byte) {
|
||||
d0 := &data[0]
|
||||
syscall.Syscall9(_glReadPixels.Addr(), 7, uintptr(x), uintptr(y), uintptr(width), uintptr(height), uintptr(format), uintptr(ty), uintptr(unsafe.Pointer(d0)), 0, 0)
|
||||
issue34474KeepAlive(d0)
|
||||
}
|
||||
|
||||
func (c *Functions) RenderbufferStorage(target, internalformat Enum, width, height int) {
|
||||
syscall.Syscall6(_glRenderbufferStorage.Addr(), 4, uintptr(target), uintptr(internalformat), uintptr(width), uintptr(height), 0, 0)
|
||||
}
|
||||
|
||||
func (c *Functions) Scissor(x, y, width, height int32) {
|
||||
syscall.Syscall6(_glScissor.Addr(), 4, uintptr(x), uintptr(y), uintptr(width), uintptr(height), 0, 0)
|
||||
}
|
||||
|
||||
func (c *Functions) ShaderSource(s Shader, src string) {
|
||||
var n uintptr = uintptr(len(src))
|
||||
psrc := &src
|
||||
syscall.Syscall6(_glShaderSource.Addr(), 4, uintptr(s.V), 1, uintptr(unsafe.Pointer(psrc)), uintptr(unsafe.Pointer(&n)), 0, 0)
|
||||
issue34474KeepAlive(psrc)
|
||||
}
|
||||
|
||||
func (f *Functions) TexImage2D(target Enum, level int, internalFormat Enum, width int, height int, format Enum, ty Enum) {
|
||||
syscall.Syscall9(_glTexImage2D.Addr(), 9, uintptr(target), uintptr(level), uintptr(internalFormat), uintptr(width), uintptr(height), 0, uintptr(format), uintptr(ty), 0)
|
||||
}
|
||||
|
||||
func (f *Functions) TexStorage2D(target Enum, levels int, internalFormat Enum, width, height int) {
|
||||
syscall.Syscall6(_glTexStorage2D.Addr(), 5, uintptr(target), uintptr(levels), uintptr(internalFormat), uintptr(width), uintptr(height), 0)
|
||||
}
|
||||
|
||||
func (c *Functions) TexSubImage2D(target Enum, level int, x, y, width, height int, format, ty Enum, data []byte) {
|
||||
d0 := &data[0]
|
||||
syscall.Syscall9(_glTexSubImage2D.Addr(), 9, uintptr(target), uintptr(level), uintptr(x), uintptr(y), uintptr(width), uintptr(height), uintptr(format), uintptr(ty), uintptr(unsafe.Pointer(d0)))
|
||||
issue34474KeepAlive(d0)
|
||||
}
|
||||
|
||||
func (c *Functions) TexParameteri(target, pname Enum, param int) {
|
||||
syscall.Syscall(_glTexParameteri.Addr(), 3, uintptr(target), uintptr(pname), uintptr(param))
|
||||
}
|
||||
|
||||
func (f *Functions) UniformBlockBinding(p Program, uniformBlockIndex uint, uniformBlockBinding uint) {
|
||||
syscall.Syscall(_glUniformBlockBinding.Addr(), 3, uintptr(p.V), uintptr(uniformBlockIndex), uintptr(uniformBlockBinding))
|
||||
}
|
||||
|
||||
func (c *Functions) Uniform1f(dst Uniform, v float32) {
|
||||
syscall.Syscall(_glUniform1f.Addr(), 2, uintptr(dst.V), uintptr(math.Float32bits(v)), 0)
|
||||
}
|
||||
|
||||
func (c *Functions) Uniform1i(dst Uniform, v int) {
|
||||
syscall.Syscall(_glUniform1i.Addr(), 2, uintptr(dst.V), uintptr(v), 0)
|
||||
}
|
||||
|
||||
func (c *Functions) Uniform2f(dst Uniform, v0, v1 float32) {
|
||||
syscall.Syscall(_glUniform2f.Addr(), 3, uintptr(dst.V), uintptr(math.Float32bits(v0)), uintptr(math.Float32bits(v1)))
|
||||
}
|
||||
|
||||
func (c *Functions) Uniform3f(dst Uniform, v0, v1, v2 float32) {
|
||||
syscall.Syscall6(_glUniform3f.Addr(), 4, uintptr(dst.V), uintptr(math.Float32bits(v0)), uintptr(math.Float32bits(v1)), uintptr(math.Float32bits(v2)), 0, 0)
|
||||
}
|
||||
|
||||
func (c *Functions) Uniform4f(dst Uniform, v0, v1, v2, v3 float32) {
|
||||
syscall.Syscall6(_glUniform4f.Addr(), 5, uintptr(dst.V), uintptr(math.Float32bits(v0)), uintptr(math.Float32bits(v1)), uintptr(math.Float32bits(v2)), uintptr(math.Float32bits(v3)), 0)
|
||||
}
|
||||
|
||||
func (c *Functions) UseProgram(p Program) {
|
||||
syscall.Syscall(_glUseProgram.Addr(), 1, uintptr(p.V), 0, 0)
|
||||
}
|
||||
|
||||
func (f *Functions) UnmapBuffer(target Enum) bool {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (c *Functions) VertexAttribPointer(dst Attrib, size int, ty Enum, normalized bool, stride, offset int) {
|
||||
var norm uintptr
|
||||
if normalized {
|
||||
norm = 1
|
||||
}
|
||||
syscall.Syscall6(_glVertexAttribPointer.Addr(), 6, uintptr(dst), uintptr(size), uintptr(ty), norm, uintptr(stride), uintptr(offset))
|
||||
}
|
||||
|
||||
func (c *Functions) Viewport(x, y, width, height int) {
|
||||
syscall.Syscall6(_glViewport.Addr(), 4, uintptr(x), uintptr(y), uintptr(width), uintptr(height), 0, 0)
|
||||
}
|
||||
|
||||
func cString(s string) []byte {
|
||||
b := make([]byte, len(s)+1)
|
||||
copy(b, s)
|
||||
return b
|
||||
}
|
||||
|
||||
// issue34474KeepAlive calls runtime.KeepAlive as a
|
||||
// workaround for golang.org/issue/34474.
|
||||
func issue34474KeepAlive(v any) {
|
||||
runtime.KeepAlive(v)
|
||||
}
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
//go:build !js
|
||||
|
||||
package gl
|
||||
|
||||
type (
|
||||
Object struct{ V uint }
|
||||
Buffer Object
|
||||
Framebuffer Object
|
||||
Program Object
|
||||
Renderbuffer Object
|
||||
Shader Object
|
||||
Texture Object
|
||||
Query Object
|
||||
Uniform struct{ V int }
|
||||
VertexArray Object
|
||||
)
|
||||
|
||||
func (o Object) valid() bool {
|
||||
return o.V != 0
|
||||
}
|
||||
|
||||
func (o Object) equal(o2 Object) bool {
|
||||
return o == o2
|
||||
}
|
||||
|
||||
func (u Framebuffer) Valid() bool {
|
||||
return Object(u).valid()
|
||||
}
|
||||
|
||||
func (u Uniform) Valid() bool {
|
||||
return u.V != -1
|
||||
}
|
||||
|
||||
func (p Program) Valid() bool {
|
||||
return Object(p).valid()
|
||||
}
|
||||
|
||||
func (s Shader) Valid() bool {
|
||||
return Object(s).valid()
|
||||
}
|
||||
|
||||
func (a VertexArray) Valid() bool {
|
||||
return Object(a).valid()
|
||||
}
|
||||
|
||||
func (f Framebuffer) Equal(f2 Framebuffer) bool {
|
||||
return Object(f).equal(Object(f2))
|
||||
}
|
||||
|
||||
func (p Program) Equal(p2 Program) bool {
|
||||
return Object(p).equal(Object(p2))
|
||||
}
|
||||
|
||||
func (s Shader) Equal(s2 Shader) bool {
|
||||
return Object(s).equal(Object(s2))
|
||||
}
|
||||
|
||||
func (u Uniform) Equal(u2 Uniform) bool {
|
||||
return u == u2
|
||||
}
|
||||
|
||||
func (a VertexArray) Equal(a2 VertexArray) bool {
|
||||
return Object(a).equal(Object(a2))
|
||||
}
|
||||
|
||||
func (r Renderbuffer) Equal(r2 Renderbuffer) bool {
|
||||
return Object(r).equal(Object(r2))
|
||||
}
|
||||
|
||||
func (t Texture) Equal(t2 Texture) bool {
|
||||
return Object(t).equal(Object(t2))
|
||||
}
|
||||
|
||||
func (b Buffer) Equal(b2 Buffer) bool {
|
||||
return Object(b).equal(Object(b2))
|
||||
}
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
// SPDX-License-Identifier: Unlicense OR MIT
|
||||
|
||||
package gl
|
||||
|
||||
import "syscall/js"
|
||||
|
||||
type (
|
||||
Object js.Value
|
||||
Buffer Object
|
||||
Framebuffer Object
|
||||
Program Object
|
||||
Renderbuffer Object
|
||||
Shader Object
|
||||
Texture Object
|
||||
Query Object
|
||||
Uniform Object
|
||||
VertexArray Object
|
||||
)
|
||||
|
||||
func (o Object) valid() bool {
|
||||
return js.Value(o).Truthy()
|
||||
}
|
||||
|
||||
func (o Object) equal(o2 Object) bool {
|
||||
return js.Value(o).Equal(js.Value(o2))
|
||||
}
|
||||
|
||||
func (b Buffer) Valid() bool {
|
||||
return Object(b).valid()
|
||||
}
|
||||
|
||||
func (f Framebuffer) Valid() bool {
|
||||
return Object(f).valid()
|
||||
}
|
||||
|
||||
func (p Program) Valid() bool {
|
||||
return Object(p).valid()
|
||||
}
|
||||
|
||||
func (r Renderbuffer) Valid() bool {
|
||||
return Object(r).valid()
|
||||
}
|
||||
|
||||
func (s Shader) Valid() bool {
|
||||
return Object(s).valid()
|
||||
}
|
||||
|
||||
func (t Texture) Valid() bool {
|
||||
return Object(t).valid()
|
||||
}
|
||||
|
||||
func (u Uniform) Valid() bool {
|
||||
return Object(u).valid()
|
||||
}
|
||||
|
||||
func (a VertexArray) Valid() bool {
|
||||
return Object(a).valid()
|
||||
}
|
||||
|
||||
func (f Framebuffer) Equal(f2 Framebuffer) bool {
|
||||
return Object(f).equal(Object(f2))
|
||||
}
|
||||
|
||||
func (p Program) Equal(p2 Program) bool {
|
||||
return Object(p).equal(Object(p2))
|
||||
}
|
||||
|
||||
func (s Shader) Equal(s2 Shader) bool {
|
||||
return Object(s).equal(Object(s2))
|
||||
}
|
||||
|
||||
func (u Uniform) Equal(u2 Uniform) bool {
|
||||
return Object(u).equal(Object(u2))
|
||||
}
|
||||
|
||||
func (a VertexArray) Equal(a2 VertexArray) bool {
|
||||
return Object(a).equal(Object(a2))
|
||||
}
|
||||
|
||||
func (r Renderbuffer) Equal(r2 Renderbuffer) bool {
|
||||
return Object(r).equal(Object(r2))
|
||||
}
|
||||
|
||||
func (t Texture) Equal(t2 Texture) bool {
|
||||
return Object(t).equal(Object(t2))
|
||||
}
|
||||
|
||||
func (b Buffer) Equal(b2 Buffer) bool {
|
||||
return Object(b).equal(Object(b2))
|
||||
}
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
// SPDX-License-Identifier: Unlicense OR MIT
|
||||
|
||||
package gl
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func CreateProgram(ctx *Functions, vsSrc, fsSrc string, attribs []string) (Program, error) {
|
||||
vs, err := CreateShader(ctx, VERTEX_SHADER, vsSrc)
|
||||
if err != nil {
|
||||
return Program{}, err
|
||||
}
|
||||
defer ctx.DeleteShader(vs)
|
||||
fs, err := CreateShader(ctx, FRAGMENT_SHADER, fsSrc)
|
||||
if err != nil {
|
||||
return Program{}, err
|
||||
}
|
||||
defer ctx.DeleteShader(fs)
|
||||
prog := ctx.CreateProgram()
|
||||
if !prog.Valid() {
|
||||
return Program{}, errors.New("glCreateProgram failed")
|
||||
}
|
||||
ctx.AttachShader(prog, vs)
|
||||
ctx.AttachShader(prog, fs)
|
||||
for i, a := range attribs {
|
||||
ctx.BindAttribLocation(prog, Attrib(i), a)
|
||||
}
|
||||
ctx.LinkProgram(prog)
|
||||
if ctx.GetProgrami(prog, LINK_STATUS) == 0 {
|
||||
log := ctx.GetProgramInfoLog(prog)
|
||||
ctx.DeleteProgram(prog)
|
||||
return Program{}, fmt.Errorf("program link failed: %s", strings.TrimSpace(log))
|
||||
}
|
||||
return prog, nil
|
||||
}
|
||||
|
||||
func CreateComputeProgram(ctx *Functions, src string) (Program, error) {
|
||||
cs, err := CreateShader(ctx, COMPUTE_SHADER, src)
|
||||
if err != nil {
|
||||
return Program{}, err
|
||||
}
|
||||
defer ctx.DeleteShader(cs)
|
||||
prog := ctx.CreateProgram()
|
||||
if !prog.Valid() {
|
||||
return Program{}, errors.New("glCreateProgram failed")
|
||||
}
|
||||
ctx.AttachShader(prog, cs)
|
||||
ctx.LinkProgram(prog)
|
||||
if ctx.GetProgrami(prog, LINK_STATUS) == 0 {
|
||||
log := ctx.GetProgramInfoLog(prog)
|
||||
ctx.DeleteProgram(prog)
|
||||
return Program{}, fmt.Errorf("program link failed: %s", strings.TrimSpace(log))
|
||||
}
|
||||
return prog, nil
|
||||
}
|
||||
|
||||
func CreateShader(ctx *Functions, typ Enum, src string) (Shader, error) {
|
||||
sh := ctx.CreateShader(typ)
|
||||
if !sh.Valid() {
|
||||
return Shader{}, errors.New("glCreateShader failed")
|
||||
}
|
||||
ctx.ShaderSource(sh, src)
|
||||
ctx.CompileShader(sh)
|
||||
if ctx.GetShaderi(sh, COMPILE_STATUS) == 0 {
|
||||
log := ctx.GetShaderInfoLog(sh)
|
||||
ctx.DeleteShader(sh)
|
||||
return Shader{}, fmt.Errorf("shader compilation failed: %s", strings.TrimSpace(log))
|
||||
}
|
||||
return sh, nil
|
||||
}
|
||||
|
||||
func ParseGLVersion(glVer string) (version [2]int, gles bool, err error) {
|
||||
var ver [2]int
|
||||
if _, err := fmt.Sscanf(glVer, "OpenGL ES %d.%d", &ver[0], &ver[1]); err == nil {
|
||||
return ver, true, nil
|
||||
} else if _, err := fmt.Sscanf(glVer, "WebGL %d.%d", &ver[0], &ver[1]); err == nil {
|
||||
// WebGL major version v corresponds to OpenGL ES version v + 1
|
||||
ver[0]++
|
||||
return ver, true, nil
|
||||
} else if _, err := fmt.Sscanf(glVer, "%d.%d", &ver[0], &ver[1]); err == nil {
|
||||
return ver, false, nil
|
||||
}
|
||||
return ver, false, fmt.Errorf("failed to parse OpenGL ES version (%s)", glVer)
|
||||
}
|
||||
+497
@@ -0,0 +1,497 @@
|
||||
// SPDX-License-Identifier: Unlicense OR MIT
|
||||
|
||||
package ops
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"image"
|
||||
"math"
|
||||
|
||||
"gioui.org/f32"
|
||||
"gioui.org/internal/byteslice"
|
||||
"gioui.org/internal/scene"
|
||||
)
|
||||
|
||||
type Ops struct {
|
||||
// version is incremented at each Reset.
|
||||
version uint32
|
||||
// data contains the serialized operations.
|
||||
data []byte
|
||||
// refs hold external references for operations.
|
||||
refs []any
|
||||
// stringRefs provides space for string references, pointers to which will
|
||||
// be stored in refs. Storing a string directly in refs would cause a heap
|
||||
// allocation, to store the string header in an interface value. The backing
|
||||
// array of stringRefs, on the other hand, gets reused between calls to
|
||||
// reset, making string references free on average.
|
||||
//
|
||||
// Appending to stringRefs might reallocate the backing array, which will
|
||||
// leave pointers to the old array in refs. This temporarily causes a slight
|
||||
// increase in memory usage, but this, too, amortizes away as the capacity
|
||||
// of stringRefs approaches its stable maximum.
|
||||
stringRefs []string
|
||||
// nextStateID is the id allocated for the next
|
||||
// StateOp.
|
||||
nextStateID uint32
|
||||
// multipOp indicates a multi-op such as clip.Path is being added.
|
||||
multipOp bool
|
||||
|
||||
macroStack stack
|
||||
stacks [_StackKind]stack
|
||||
}
|
||||
|
||||
type OpType byte
|
||||
|
||||
type Shape byte
|
||||
|
||||
// Start at a high number for easier debugging.
|
||||
const firstOpIndex = 200
|
||||
|
||||
const (
|
||||
TypeMacro OpType = iota + firstOpIndex
|
||||
TypeCall
|
||||
TypeDefer
|
||||
TypeTransform
|
||||
TypePopTransform
|
||||
TypePushOpacity
|
||||
TypePopOpacity
|
||||
TypeImage
|
||||
TypePaint
|
||||
TypeColor
|
||||
TypeLinearGradient
|
||||
TypePass
|
||||
TypePopPass
|
||||
TypeInput
|
||||
TypeKeyInputHint
|
||||
TypeSave
|
||||
TypeLoad
|
||||
TypeAux
|
||||
TypeClip
|
||||
TypePopClip
|
||||
TypeCursor
|
||||
TypePath
|
||||
TypeStroke
|
||||
TypeSemanticLabel
|
||||
TypeSemanticDesc
|
||||
TypeSemanticClass
|
||||
TypeSemanticSelected
|
||||
TypeSemanticEnabled
|
||||
TypeActionInput
|
||||
)
|
||||
|
||||
type StackID struct {
|
||||
id uint32
|
||||
prev uint32
|
||||
}
|
||||
|
||||
// StateOp represents a saved operation snapshot to be restored
|
||||
// later.
|
||||
type StateOp struct {
|
||||
id uint32
|
||||
macroID uint32
|
||||
ops *Ops
|
||||
}
|
||||
|
||||
// stack tracks the integer identities of stack operations to ensure correct
|
||||
// pairing of their push and pop methods.
|
||||
type stack struct {
|
||||
currentID uint32
|
||||
nextID uint32
|
||||
}
|
||||
|
||||
type StackKind uint8
|
||||
|
||||
// ClipOp is the shadow of clip.Op.
|
||||
type ClipOp struct {
|
||||
Bounds image.Rectangle
|
||||
Outline bool
|
||||
Shape Shape
|
||||
}
|
||||
|
||||
const (
|
||||
ClipStack StackKind = iota
|
||||
TransStack
|
||||
PassStack
|
||||
OpacityStack
|
||||
_StackKind
|
||||
)
|
||||
|
||||
const (
|
||||
Path Shape = iota
|
||||
Ellipse
|
||||
Rect
|
||||
)
|
||||
|
||||
const (
|
||||
TypeMacroLen = 1 + 4 + 4
|
||||
TypeCallLen = 1 + 4 + 4 + 4 + 4
|
||||
TypeDeferLen = 1
|
||||
TypeTransformLen = 1 + 1 + 4*6
|
||||
TypePopTransformLen = 1
|
||||
TypePushOpacityLen = 1 + 4
|
||||
TypePopOpacityLen = 1
|
||||
TypeRedrawLen = 1 + 8
|
||||
TypeImageLen = 1 + 1
|
||||
TypePaintLen = 1
|
||||
TypeColorLen = 1 + 4
|
||||
TypeLinearGradientLen = 1 + 8*2 + 4*2
|
||||
TypePassLen = 1
|
||||
TypePopPassLen = 1
|
||||
TypeInputLen = 1
|
||||
TypeKeyInputHintLen = 1 + 1
|
||||
TypeSaveLen = 1 + 4
|
||||
TypeLoadLen = 1 + 4
|
||||
TypeAuxLen = 1
|
||||
TypeClipLen = 1 + 4*4 + 1 + 1
|
||||
TypePopClipLen = 1
|
||||
TypeCursorLen = 2
|
||||
TypePathLen = 8 + 1
|
||||
TypeStrokeLen = 1 + 4
|
||||
TypeSemanticLabelLen = 1
|
||||
TypeSemanticDescLen = 1
|
||||
TypeSemanticClassLen = 2
|
||||
TypeSemanticSelectedLen = 2
|
||||
TypeSemanticEnabledLen = 2
|
||||
TypeActionInputLen = 1 + 1
|
||||
)
|
||||
|
||||
func (op *ClipOp) Decode(data []byte) {
|
||||
if len(data) < TypeClipLen || OpType(data[0]) != TypeClip {
|
||||
panic("invalid op")
|
||||
}
|
||||
data = data[:TypeClipLen]
|
||||
bo := binary.LittleEndian
|
||||
op.Bounds.Min.X = int(int32(bo.Uint32(data[1:])))
|
||||
op.Bounds.Min.Y = int(int32(bo.Uint32(data[5:])))
|
||||
op.Bounds.Max.X = int(int32(bo.Uint32(data[9:])))
|
||||
op.Bounds.Max.Y = int(int32(bo.Uint32(data[13:])))
|
||||
op.Outline = data[17] == 1
|
||||
op.Shape = Shape(data[18])
|
||||
}
|
||||
|
||||
func Reset(o *Ops) {
|
||||
o.macroStack = stack{}
|
||||
o.stacks = [_StackKind]stack{}
|
||||
// Leave references to the GC.
|
||||
for i := range o.refs {
|
||||
o.refs[i] = nil
|
||||
}
|
||||
for i := range o.stringRefs {
|
||||
o.stringRefs[i] = ""
|
||||
}
|
||||
o.data = o.data[:0]
|
||||
o.refs = o.refs[:0]
|
||||
o.stringRefs = o.stringRefs[:0]
|
||||
o.nextStateID = 0
|
||||
o.version++
|
||||
}
|
||||
|
||||
func Write(o *Ops, n int) []byte {
|
||||
if o.multipOp {
|
||||
panic("cannot mix multi ops with single ones")
|
||||
}
|
||||
o.data = append(o.data, make([]byte, n)...)
|
||||
return o.data[len(o.data)-n:]
|
||||
}
|
||||
|
||||
func BeginMulti(o *Ops) {
|
||||
if o.multipOp {
|
||||
panic("cannot interleave multi ops")
|
||||
}
|
||||
o.multipOp = true
|
||||
}
|
||||
|
||||
func EndMulti(o *Ops) {
|
||||
if !o.multipOp {
|
||||
panic("cannot end non multi ops")
|
||||
}
|
||||
o.multipOp = false
|
||||
}
|
||||
|
||||
func WriteMulti(o *Ops, n int) []byte {
|
||||
if !o.multipOp {
|
||||
panic("cannot use multi ops in single ops")
|
||||
}
|
||||
o.data = append(o.data, make([]byte, n)...)
|
||||
return o.data[len(o.data)-n:]
|
||||
}
|
||||
|
||||
func PushMacro(o *Ops) StackID {
|
||||
return o.macroStack.push()
|
||||
}
|
||||
|
||||
func PopMacro(o *Ops, id StackID) {
|
||||
o.macroStack.pop(id)
|
||||
}
|
||||
|
||||
func FillMacro(o *Ops, startPC PC) {
|
||||
pc := PCFor(o)
|
||||
// Fill out the macro definition reserved in Record.
|
||||
data := o.data[startPC.data:]
|
||||
data = data[:TypeMacroLen]
|
||||
data[0] = byte(TypeMacro)
|
||||
bo := binary.LittleEndian
|
||||
bo.PutUint32(data[1:], uint32(pc.data))
|
||||
bo.PutUint32(data[5:], uint32(pc.refs))
|
||||
}
|
||||
|
||||
func AddCall(o *Ops, callOps *Ops, pc PC, end PC) {
|
||||
data := Write1(o, TypeCallLen, callOps)
|
||||
data[0] = byte(TypeCall)
|
||||
bo := binary.LittleEndian
|
||||
bo.PutUint32(data[1:], uint32(pc.data))
|
||||
bo.PutUint32(data[5:], uint32(pc.refs))
|
||||
bo.PutUint32(data[9:], uint32(end.data))
|
||||
bo.PutUint32(data[13:], uint32(end.refs))
|
||||
}
|
||||
|
||||
func PushOp(o *Ops, kind StackKind) (StackID, uint32) {
|
||||
return o.stacks[kind].push(), o.macroStack.currentID
|
||||
}
|
||||
|
||||
func PopOp(o *Ops, kind StackKind, sid StackID, macroID uint32) {
|
||||
if o.macroStack.currentID != macroID {
|
||||
panic("stack push and pop must not cross macro boundary")
|
||||
}
|
||||
o.stacks[kind].pop(sid)
|
||||
}
|
||||
|
||||
func Write1(o *Ops, n int, ref1 any) []byte {
|
||||
o.data = append(o.data, make([]byte, n)...)
|
||||
o.refs = append(o.refs, ref1)
|
||||
return o.data[len(o.data)-n:]
|
||||
}
|
||||
|
||||
func Write1String(o *Ops, n int, ref1 string) []byte {
|
||||
o.data = append(o.data, make([]byte, n)...)
|
||||
o.stringRefs = append(o.stringRefs, ref1)
|
||||
o.refs = append(o.refs, &o.stringRefs[len(o.stringRefs)-1])
|
||||
return o.data[len(o.data)-n:]
|
||||
}
|
||||
|
||||
func Write2(o *Ops, n int, ref1, ref2 any) []byte {
|
||||
o.data = append(o.data, make([]byte, n)...)
|
||||
o.refs = append(o.refs, ref1, ref2)
|
||||
return o.data[len(o.data)-n:]
|
||||
}
|
||||
|
||||
func Write2String(o *Ops, n int, ref1 any, ref2 string) []byte {
|
||||
o.data = append(o.data, make([]byte, n)...)
|
||||
o.stringRefs = append(o.stringRefs, ref2)
|
||||
o.refs = append(o.refs, ref1, &o.stringRefs[len(o.stringRefs)-1])
|
||||
return o.data[len(o.data)-n:]
|
||||
}
|
||||
|
||||
func Write3(o *Ops, n int, ref1, ref2, ref3 any) []byte {
|
||||
o.data = append(o.data, make([]byte, n)...)
|
||||
o.refs = append(o.refs, ref1, ref2, ref3)
|
||||
return o.data[len(o.data)-n:]
|
||||
}
|
||||
|
||||
func PCFor(o *Ops) PC {
|
||||
return PC{data: uint32(len(o.data)), refs: uint32(len(o.refs))}
|
||||
}
|
||||
|
||||
func (s *stack) push() StackID {
|
||||
s.nextID++
|
||||
sid := StackID{
|
||||
id: s.nextID,
|
||||
prev: s.currentID,
|
||||
}
|
||||
s.currentID = s.nextID
|
||||
return sid
|
||||
}
|
||||
|
||||
func (s *stack) check(sid StackID) {
|
||||
if s.currentID != sid.id {
|
||||
panic("unbalanced operation")
|
||||
}
|
||||
}
|
||||
|
||||
func (s *stack) pop(sid StackID) {
|
||||
s.check(sid)
|
||||
s.currentID = sid.prev
|
||||
}
|
||||
|
||||
// Save the effective transformation.
|
||||
func Save(o *Ops) StateOp {
|
||||
o.nextStateID++
|
||||
s := StateOp{
|
||||
ops: o,
|
||||
id: o.nextStateID,
|
||||
macroID: o.macroStack.currentID,
|
||||
}
|
||||
bo := binary.LittleEndian
|
||||
data := Write(o, TypeSaveLen)
|
||||
data[0] = byte(TypeSave)
|
||||
bo.PutUint32(data[1:], uint32(s.id))
|
||||
return s
|
||||
}
|
||||
|
||||
// Load a previously saved operations state given
|
||||
// its ID.
|
||||
func (s StateOp) Load() {
|
||||
bo := binary.LittleEndian
|
||||
data := Write(s.ops, TypeLoadLen)
|
||||
data[0] = byte(TypeLoad)
|
||||
bo.PutUint32(data[1:], uint32(s.id))
|
||||
}
|
||||
|
||||
func DecodeCommand(d []byte) scene.Command {
|
||||
var cmd scene.Command
|
||||
copy(byteslice.Uint32(cmd[:]), d)
|
||||
return cmd
|
||||
}
|
||||
|
||||
func EncodeCommand(out []byte, cmd scene.Command) {
|
||||
copy(out, byteslice.Uint32(cmd[:]))
|
||||
}
|
||||
|
||||
func DecodeTransform(data []byte) (t f32.Affine2D, push bool) {
|
||||
if OpType(data[0]) != TypeTransform {
|
||||
panic("invalid op")
|
||||
}
|
||||
push = data[1] != 0
|
||||
data = data[2:]
|
||||
data = data[:4*6]
|
||||
|
||||
bo := binary.LittleEndian
|
||||
a := math.Float32frombits(bo.Uint32(data))
|
||||
b := math.Float32frombits(bo.Uint32(data[4*1:]))
|
||||
c := math.Float32frombits(bo.Uint32(data[4*2:]))
|
||||
d := math.Float32frombits(bo.Uint32(data[4*3:]))
|
||||
e := math.Float32frombits(bo.Uint32(data[4*4:]))
|
||||
f := math.Float32frombits(bo.Uint32(data[4*5:]))
|
||||
return f32.NewAffine2D(a, b, c, d, e, f), push
|
||||
}
|
||||
|
||||
func DecodeOpacity(data []byte) float32 {
|
||||
if OpType(data[0]) != TypePushOpacity {
|
||||
panic("invalid op")
|
||||
}
|
||||
bo := binary.LittleEndian
|
||||
return math.Float32frombits(bo.Uint32(data[1:]))
|
||||
}
|
||||
|
||||
// DecodeSave decodes the state id of a save op.
|
||||
func DecodeSave(data []byte) int {
|
||||
if OpType(data[0]) != TypeSave {
|
||||
panic("invalid op")
|
||||
}
|
||||
bo := binary.LittleEndian
|
||||
return int(bo.Uint32(data[1:]))
|
||||
}
|
||||
|
||||
// DecodeLoad decodes the state id of a load op.
|
||||
func DecodeLoad(data []byte) int {
|
||||
if OpType(data[0]) != TypeLoad {
|
||||
panic("invalid op")
|
||||
}
|
||||
bo := binary.LittleEndian
|
||||
return int(bo.Uint32(data[1:]))
|
||||
}
|
||||
|
||||
type opProp struct {
|
||||
Size byte
|
||||
NumRefs byte
|
||||
}
|
||||
|
||||
var opProps = [0x100]opProp{
|
||||
TypeMacro: {Size: TypeMacroLen, NumRefs: 0},
|
||||
TypeCall: {Size: TypeCallLen, NumRefs: 1},
|
||||
TypeDefer: {Size: TypeDeferLen, NumRefs: 0},
|
||||
TypeTransform: {Size: TypeTransformLen, NumRefs: 0},
|
||||
TypePopTransform: {Size: TypePopTransformLen, NumRefs: 0},
|
||||
TypePushOpacity: {Size: TypePushOpacityLen, NumRefs: 0},
|
||||
TypePopOpacity: {Size: TypePopOpacityLen, NumRefs: 0},
|
||||
TypeImage: {Size: TypeImageLen, NumRefs: 2},
|
||||
TypePaint: {Size: TypePaintLen, NumRefs: 0},
|
||||
TypeColor: {Size: TypeColorLen, NumRefs: 0},
|
||||
TypeLinearGradient: {Size: TypeLinearGradientLen, NumRefs: 0},
|
||||
TypePass: {Size: TypePassLen, NumRefs: 0},
|
||||
TypePopPass: {Size: TypePopPassLen, NumRefs: 0},
|
||||
TypeInput: {Size: TypeInputLen, NumRefs: 1},
|
||||
TypeKeyInputHint: {Size: TypeKeyInputHintLen, NumRefs: 1},
|
||||
TypeSave: {Size: TypeSaveLen, NumRefs: 0},
|
||||
TypeLoad: {Size: TypeLoadLen, NumRefs: 0},
|
||||
TypeAux: {Size: TypeAuxLen, NumRefs: 0},
|
||||
TypeClip: {Size: TypeClipLen, NumRefs: 0},
|
||||
TypePopClip: {Size: TypePopClipLen, NumRefs: 0},
|
||||
TypeCursor: {Size: TypeCursorLen, NumRefs: 0},
|
||||
TypePath: {Size: TypePathLen, NumRefs: 0},
|
||||
TypeStroke: {Size: TypeStrokeLen, NumRefs: 0},
|
||||
TypeSemanticLabel: {Size: TypeSemanticLabelLen, NumRefs: 1},
|
||||
TypeSemanticDesc: {Size: TypeSemanticDescLen, NumRefs: 1},
|
||||
TypeSemanticClass: {Size: TypeSemanticClassLen, NumRefs: 0},
|
||||
TypeSemanticSelected: {Size: TypeSemanticSelectedLen, NumRefs: 0},
|
||||
TypeSemanticEnabled: {Size: TypeSemanticEnabledLen, NumRefs: 0},
|
||||
TypeActionInput: {Size: TypeActionInputLen, NumRefs: 0},
|
||||
}
|
||||
|
||||
func (t OpType) props() (size, numRefs uint32) {
|
||||
v := opProps[t]
|
||||
return uint32(v.Size), uint32(v.NumRefs)
|
||||
}
|
||||
|
||||
func (t OpType) Size() uint32 {
|
||||
return uint32(opProps[t].Size)
|
||||
}
|
||||
|
||||
func (t OpType) NumRefs() uint32 {
|
||||
return uint32(opProps[t].NumRefs)
|
||||
}
|
||||
|
||||
func (t OpType) String() string {
|
||||
switch t {
|
||||
case TypeMacro:
|
||||
return "Macro"
|
||||
case TypeCall:
|
||||
return "Call"
|
||||
case TypeDefer:
|
||||
return "Defer"
|
||||
case TypeTransform:
|
||||
return "Transform"
|
||||
case TypePopTransform:
|
||||
return "PopTransform"
|
||||
case TypePushOpacity:
|
||||
return "PushOpacity"
|
||||
case TypePopOpacity:
|
||||
return "PopOpacity"
|
||||
case TypeImage:
|
||||
return "Image"
|
||||
case TypePaint:
|
||||
return "Paint"
|
||||
case TypeColor:
|
||||
return "Color"
|
||||
case TypeLinearGradient:
|
||||
return "LinearGradient"
|
||||
case TypePass:
|
||||
return "Pass"
|
||||
case TypePopPass:
|
||||
return "PopPass"
|
||||
case TypeInput:
|
||||
return "Input"
|
||||
case TypeKeyInputHint:
|
||||
return "KeyInputHint"
|
||||
case TypeSave:
|
||||
return "Save"
|
||||
case TypeLoad:
|
||||
return "Load"
|
||||
case TypeAux:
|
||||
return "Aux"
|
||||
case TypeClip:
|
||||
return "Clip"
|
||||
case TypePopClip:
|
||||
return "PopClip"
|
||||
case TypeCursor:
|
||||
return "Cursor"
|
||||
case TypePath:
|
||||
return "Path"
|
||||
case TypeStroke:
|
||||
return "Stroke"
|
||||
case TypeSemanticLabel:
|
||||
return "SemanticDescription"
|
||||
default:
|
||||
panic("unknown OpType")
|
||||
}
|
||||
}
|
||||
+190
@@ -0,0 +1,190 @@
|
||||
// SPDX-License-Identifier: Unlicense OR MIT
|
||||
|
||||
package ops
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
)
|
||||
|
||||
// Reader parses an ops list.
|
||||
type Reader struct {
|
||||
pc PC
|
||||
stack []macro
|
||||
ops *Ops
|
||||
deferOps Ops
|
||||
deferDone bool
|
||||
}
|
||||
|
||||
// EncodedOp represents an encoded op returned by
|
||||
// Reader.
|
||||
type EncodedOp struct {
|
||||
Key Key
|
||||
Data []byte
|
||||
Refs []any
|
||||
}
|
||||
|
||||
// Key is a unique key for a given op.
|
||||
type Key struct {
|
||||
ops *Ops
|
||||
pc uint32
|
||||
version uint32
|
||||
}
|
||||
|
||||
// Shadow of op.MacroOp.
|
||||
type macroOp struct {
|
||||
ops *Ops
|
||||
start PC
|
||||
end PC
|
||||
}
|
||||
|
||||
// PC is an instruction counter for an operation list.
|
||||
type PC struct {
|
||||
data uint32
|
||||
refs uint32
|
||||
}
|
||||
|
||||
type macro struct {
|
||||
ops *Ops
|
||||
retPC PC
|
||||
endPC PC
|
||||
}
|
||||
|
||||
type opMacroDef struct {
|
||||
endpc PC
|
||||
}
|
||||
|
||||
func (pc PC) Add(op OpType) PC {
|
||||
size, numRefs := op.props()
|
||||
return PC{
|
||||
data: pc.data + size,
|
||||
refs: pc.refs + numRefs,
|
||||
}
|
||||
}
|
||||
|
||||
// Reset start reading from the beginning of ops.
|
||||
func (r *Reader) Reset(ops *Ops) {
|
||||
r.ResetAt(ops, PC{})
|
||||
}
|
||||
|
||||
// ResetAt is like Reset, except it starts reading from pc.
|
||||
func (r *Reader) ResetAt(ops *Ops, pc PC) {
|
||||
r.stack = r.stack[:0]
|
||||
Reset(&r.deferOps)
|
||||
r.deferDone = false
|
||||
r.pc = pc
|
||||
r.ops = ops
|
||||
}
|
||||
|
||||
func (r *Reader) Decode() (EncodedOp, bool) {
|
||||
if r.ops == nil {
|
||||
return EncodedOp{}, false
|
||||
}
|
||||
deferring := false
|
||||
for {
|
||||
if len(r.stack) > 0 {
|
||||
b := r.stack[len(r.stack)-1]
|
||||
if r.pc == b.endPC {
|
||||
r.ops = b.ops
|
||||
r.pc = b.retPC
|
||||
r.stack = r.stack[:len(r.stack)-1]
|
||||
continue
|
||||
}
|
||||
}
|
||||
data := r.ops.data
|
||||
data = data[r.pc.data:]
|
||||
refs := r.ops.refs
|
||||
if len(data) == 0 {
|
||||
if r.deferDone {
|
||||
return EncodedOp{}, false
|
||||
}
|
||||
r.deferDone = true
|
||||
// Execute deferred macros.
|
||||
r.ops = &r.deferOps
|
||||
r.pc = PC{}
|
||||
continue
|
||||
}
|
||||
key := Key{ops: r.ops, pc: r.pc.data, version: r.ops.version}
|
||||
t := OpType(data[0])
|
||||
n, nrefs := t.props()
|
||||
data = data[:n]
|
||||
refs = refs[r.pc.refs:]
|
||||
refs = refs[:nrefs]
|
||||
switch t {
|
||||
case TypeDefer:
|
||||
deferring = true
|
||||
r.pc.data += n
|
||||
r.pc.refs += nrefs
|
||||
continue
|
||||
case TypeAux:
|
||||
// An Aux operations is always wrapped in a macro, and
|
||||
// its length is the remaining space.
|
||||
block := r.stack[len(r.stack)-1]
|
||||
n += block.endPC.data - r.pc.data - TypeAuxLen
|
||||
data = data[:n]
|
||||
case TypeCall:
|
||||
if deferring {
|
||||
deferring = false
|
||||
// Copy macro for deferred execution.
|
||||
if nrefs != 1 {
|
||||
panic("internal error: unexpected number of macro refs")
|
||||
}
|
||||
deferData := Write1(&r.deferOps, int(n), refs[0])
|
||||
copy(deferData, data)
|
||||
r.pc.data += n
|
||||
r.pc.refs += nrefs
|
||||
continue
|
||||
}
|
||||
var op macroOp
|
||||
op.decode(data, refs)
|
||||
retPC := r.pc
|
||||
retPC.data += n
|
||||
retPC.refs += nrefs
|
||||
r.stack = append(r.stack, macro{
|
||||
ops: r.ops,
|
||||
retPC: retPC,
|
||||
endPC: op.end,
|
||||
})
|
||||
r.ops = op.ops
|
||||
r.pc = op.start
|
||||
continue
|
||||
case TypeMacro:
|
||||
var op opMacroDef
|
||||
op.decode(data)
|
||||
if op.endpc != (PC{}) {
|
||||
r.pc = op.endpc
|
||||
} else {
|
||||
// Treat an incomplete macro as containing all remaining ops.
|
||||
r.pc.data = uint32(len(r.ops.data))
|
||||
r.pc.refs = uint32(len(r.ops.refs))
|
||||
}
|
||||
continue
|
||||
}
|
||||
r.pc.data += n
|
||||
r.pc.refs += nrefs
|
||||
return EncodedOp{Key: key, Data: data, Refs: refs}, true
|
||||
}
|
||||
}
|
||||
|
||||
func (op *opMacroDef) decode(data []byte) {
|
||||
if len(data) < TypeMacroLen || OpType(data[0]) != TypeMacro {
|
||||
panic("invalid op")
|
||||
}
|
||||
bo := binary.LittleEndian
|
||||
data = data[:TypeMacroLen]
|
||||
op.endpc.data = bo.Uint32(data[1:])
|
||||
op.endpc.refs = bo.Uint32(data[5:])
|
||||
}
|
||||
|
||||
func (m *macroOp) decode(data []byte, refs []any) {
|
||||
if len(data) < TypeCallLen || len(refs) < 1 || OpType(data[0]) != TypeCall {
|
||||
panic("invalid op")
|
||||
}
|
||||
bo := binary.LittleEndian
|
||||
data = data[:TypeCallLen]
|
||||
|
||||
m.ops = refs[0].(*Ops)
|
||||
m.start.data = bo.Uint32(data[1:])
|
||||
m.start.refs = bo.Uint32(data[5:])
|
||||
m.end.data = bo.Uint32(data[9:])
|
||||
m.end.refs = bo.Uint32(data[13:])
|
||||
}
|
||||
+251
@@ -0,0 +1,251 @@
|
||||
// SPDX-License-Identifier: Unlicense OR MIT
|
||||
|
||||
// Package scene encodes and decodes graphics commands in the format used by the
|
||||
// compute renderer.
|
||||
package scene
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"image"
|
||||
"image/color"
|
||||
"math"
|
||||
"unsafe"
|
||||
|
||||
"gioui.org/internal/f32"
|
||||
)
|
||||
|
||||
type Op uint32
|
||||
|
||||
type Command [sceneElemSize / 4]uint32
|
||||
|
||||
// GPU commands from piet/scene.h in package gioui.org/shaders.
|
||||
const (
|
||||
OpNop Op = iota
|
||||
OpLine
|
||||
OpQuad
|
||||
OpCubic
|
||||
OpFillColor
|
||||
OpLineWidth
|
||||
OpTransform
|
||||
OpBeginClip
|
||||
OpEndClip
|
||||
OpFillImage
|
||||
OpSetFillMode
|
||||
OpGap
|
||||
)
|
||||
|
||||
// FillModes, from setup.h.
|
||||
type FillMode uint32
|
||||
|
||||
const (
|
||||
FillModeNonzero = 0
|
||||
FillModeStroke = 1
|
||||
)
|
||||
|
||||
const CommandSize = int(unsafe.Sizeof(Command{}))
|
||||
|
||||
const sceneElemSize = 36
|
||||
|
||||
func (c Command) Op() Op {
|
||||
return Op(c[0])
|
||||
}
|
||||
|
||||
func (c Command) String() string {
|
||||
switch Op(c[0]) {
|
||||
case OpNop:
|
||||
return "nop"
|
||||
case OpLine:
|
||||
from, to := DecodeLine(c)
|
||||
return fmt.Sprintf("line(%v, %v)", from, to)
|
||||
case OpGap:
|
||||
from, to := DecodeLine(c)
|
||||
return fmt.Sprintf("gap(%v, %v)", from, to)
|
||||
case OpQuad:
|
||||
from, ctrl, to := DecodeQuad(c)
|
||||
return fmt.Sprintf("quad(%v, %v, %v)", from, ctrl, to)
|
||||
case OpCubic:
|
||||
from, ctrl0, ctrl1, to := DecodeCubic(c)
|
||||
return fmt.Sprintf("cubic(%v, %v, %v, %v)", from, ctrl0, ctrl1, to)
|
||||
case OpFillColor:
|
||||
return fmt.Sprintf("fillcolor %#.8x", c[1])
|
||||
case OpLineWidth:
|
||||
return "linewidth"
|
||||
case OpTransform:
|
||||
t := f32.NewAffine2D(
|
||||
math.Float32frombits(c[1]),
|
||||
math.Float32frombits(c[3]),
|
||||
math.Float32frombits(c[5]),
|
||||
math.Float32frombits(c[2]),
|
||||
math.Float32frombits(c[4]),
|
||||
math.Float32frombits(c[6]),
|
||||
)
|
||||
return fmt.Sprintf("transform (%v)", t)
|
||||
case OpBeginClip:
|
||||
bounds := f32.Rectangle{
|
||||
Min: f32.Pt(math.Float32frombits(c[1]), math.Float32frombits(c[2])),
|
||||
Max: f32.Pt(math.Float32frombits(c[3]), math.Float32frombits(c[4])),
|
||||
}
|
||||
return fmt.Sprintf("beginclip (%v)", bounds)
|
||||
case OpEndClip:
|
||||
bounds := f32.Rectangle{
|
||||
Min: f32.Pt(math.Float32frombits(c[1]), math.Float32frombits(c[2])),
|
||||
Max: f32.Pt(math.Float32frombits(c[3]), math.Float32frombits(c[4])),
|
||||
}
|
||||
return fmt.Sprintf("endclip (%v)", bounds)
|
||||
case OpFillImage:
|
||||
return "fillimage"
|
||||
case OpSetFillMode:
|
||||
return "setfillmode"
|
||||
default:
|
||||
panic("unreachable")
|
||||
}
|
||||
}
|
||||
|
||||
func Line(start, end f32.Point) Command {
|
||||
return Command{
|
||||
0: uint32(OpLine),
|
||||
1: math.Float32bits(start.X),
|
||||
2: math.Float32bits(start.Y),
|
||||
3: math.Float32bits(end.X),
|
||||
4: math.Float32bits(end.Y),
|
||||
}
|
||||
}
|
||||
|
||||
func Gap(start, end f32.Point) Command {
|
||||
return Command{
|
||||
0: uint32(OpGap),
|
||||
1: math.Float32bits(start.X),
|
||||
2: math.Float32bits(start.Y),
|
||||
3: math.Float32bits(end.X),
|
||||
4: math.Float32bits(end.Y),
|
||||
}
|
||||
}
|
||||
|
||||
func Cubic(start, ctrl0, ctrl1, end f32.Point) Command {
|
||||
return Command{
|
||||
0: uint32(OpCubic),
|
||||
1: math.Float32bits(start.X),
|
||||
2: math.Float32bits(start.Y),
|
||||
3: math.Float32bits(ctrl0.X),
|
||||
4: math.Float32bits(ctrl0.Y),
|
||||
5: math.Float32bits(ctrl1.X),
|
||||
6: math.Float32bits(ctrl1.Y),
|
||||
7: math.Float32bits(end.X),
|
||||
8: math.Float32bits(end.Y),
|
||||
}
|
||||
}
|
||||
|
||||
func Quad(start, ctrl, end f32.Point) Command {
|
||||
return Command{
|
||||
0: uint32(OpQuad),
|
||||
1: math.Float32bits(start.X),
|
||||
2: math.Float32bits(start.Y),
|
||||
3: math.Float32bits(ctrl.X),
|
||||
4: math.Float32bits(ctrl.Y),
|
||||
5: math.Float32bits(end.X),
|
||||
6: math.Float32bits(end.Y),
|
||||
}
|
||||
}
|
||||
|
||||
func Transform(m f32.Affine2D) Command {
|
||||
sx, hx, ox, hy, sy, oy := m.Elems()
|
||||
return Command{
|
||||
0: uint32(OpTransform),
|
||||
1: math.Float32bits(sx),
|
||||
2: math.Float32bits(hy),
|
||||
3: math.Float32bits(hx),
|
||||
4: math.Float32bits(sy),
|
||||
5: math.Float32bits(ox),
|
||||
6: math.Float32bits(oy),
|
||||
}
|
||||
}
|
||||
|
||||
func SetLineWidth(width float32) Command {
|
||||
return Command{
|
||||
0: uint32(OpLineWidth),
|
||||
1: math.Float32bits(width),
|
||||
}
|
||||
}
|
||||
|
||||
func BeginClip(bbox f32.Rectangle) Command {
|
||||
return Command{
|
||||
0: uint32(OpBeginClip),
|
||||
1: math.Float32bits(bbox.Min.X),
|
||||
2: math.Float32bits(bbox.Min.Y),
|
||||
3: math.Float32bits(bbox.Max.X),
|
||||
4: math.Float32bits(bbox.Max.Y),
|
||||
}
|
||||
}
|
||||
|
||||
func EndClip(bbox f32.Rectangle) Command {
|
||||
return Command{
|
||||
0: uint32(OpEndClip),
|
||||
1: math.Float32bits(bbox.Min.X),
|
||||
2: math.Float32bits(bbox.Min.Y),
|
||||
3: math.Float32bits(bbox.Max.X),
|
||||
4: math.Float32bits(bbox.Max.Y),
|
||||
}
|
||||
}
|
||||
|
||||
func FillColor(col color.RGBA) Command {
|
||||
return Command{
|
||||
0: uint32(OpFillColor),
|
||||
1: uint32(col.R)<<24 | uint32(col.G)<<16 | uint32(col.B)<<8 | uint32(col.A),
|
||||
}
|
||||
}
|
||||
|
||||
func FillImage(index int, offset image.Point) Command {
|
||||
x := int16(offset.X)
|
||||
y := int16(offset.Y)
|
||||
return Command{
|
||||
0: uint32(OpFillImage),
|
||||
1: uint32(index),
|
||||
2: uint32(uint16(x)) | uint32(uint16(y))<<16,
|
||||
}
|
||||
}
|
||||
|
||||
func SetFillMode(mode FillMode) Command {
|
||||
return Command{
|
||||
0: uint32(OpSetFillMode),
|
||||
1: uint32(mode),
|
||||
}
|
||||
}
|
||||
|
||||
func DecodeLine(cmd Command) (from, to f32.Point) {
|
||||
if cmd[0] != uint32(OpLine) {
|
||||
panic("invalid command")
|
||||
}
|
||||
from = f32.Pt(math.Float32frombits(cmd[1]), math.Float32frombits(cmd[2]))
|
||||
to = f32.Pt(math.Float32frombits(cmd[3]), math.Float32frombits(cmd[4]))
|
||||
return
|
||||
}
|
||||
|
||||
func DecodeGap(cmd Command) (from, to f32.Point) {
|
||||
if cmd[0] != uint32(OpGap) {
|
||||
panic("invalid command")
|
||||
}
|
||||
from = f32.Pt(math.Float32frombits(cmd[1]), math.Float32frombits(cmd[2]))
|
||||
to = f32.Pt(math.Float32frombits(cmd[3]), math.Float32frombits(cmd[4]))
|
||||
return
|
||||
}
|
||||
|
||||
func DecodeQuad(cmd Command) (from, ctrl, to f32.Point) {
|
||||
if cmd[0] != uint32(OpQuad) {
|
||||
panic("invalid command")
|
||||
}
|
||||
from = f32.Pt(math.Float32frombits(cmd[1]), math.Float32frombits(cmd[2]))
|
||||
ctrl = f32.Pt(math.Float32frombits(cmd[3]), math.Float32frombits(cmd[4]))
|
||||
to = f32.Pt(math.Float32frombits(cmd[5]), math.Float32frombits(cmd[6]))
|
||||
return
|
||||
}
|
||||
|
||||
func DecodeCubic(cmd Command) (from, ctrl0, ctrl1, to f32.Point) {
|
||||
if cmd[0] != uint32(OpCubic) {
|
||||
panic("invalid command")
|
||||
}
|
||||
from = f32.Pt(math.Float32frombits(cmd[1]), math.Float32frombits(cmd[2]))
|
||||
ctrl0 = f32.Pt(math.Float32frombits(cmd[3]), math.Float32frombits(cmd[4]))
|
||||
ctrl1 = f32.Pt(math.Float32frombits(cmd[5]), math.Float32frombits(cmd[6]))
|
||||
to = f32.Pt(math.Float32frombits(cmd[7]), math.Float32frombits(cmd[8]))
|
||||
return
|
||||
}
|
||||
+760
@@ -0,0 +1,760 @@
|
||||
// SPDX-License-Identifier: Unlicense OR MIT
|
||||
|
||||
// Most of the algorithms to compute strokes and their offsets have been
|
||||
// extracted, adapted from (and used as a reference implementation):
|
||||
// - github.com/tdewolff/canvas (Licensed under MIT)
|
||||
//
|
||||
// These algorithms have been implemented from:
|
||||
// Fast, precise flattening of cubic Bézier path and offset curves
|
||||
// Thomas F. Hain, et al.
|
||||
//
|
||||
// An electronic version is available at:
|
||||
// https://seant23.files.wordpress.com/2010/11/fastpreciseflatteningofbeziercurve.pdf
|
||||
//
|
||||
// Possible improvements (in term of speed and/or accuracy) on these
|
||||
// algorithms are:
|
||||
//
|
||||
// - Polar Stroking: New Theory and Methods for Stroking Paths,
|
||||
// M. Kilgard
|
||||
// https://arxiv.org/pdf/2007.00308.pdf
|
||||
//
|
||||
// - https://raphlinus.github.io/graphics/curves/2019/12/23/flatten-quadbez.html
|
||||
// R. Levien
|
||||
|
||||
// Package stroke implements conversion of strokes to filled outlines. It is used as a
|
||||
// fallback for stroke configurations not natively supported by the renderer.
|
||||
package stroke
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"math"
|
||||
|
||||
"gioui.org/internal/f32"
|
||||
"gioui.org/internal/ops"
|
||||
"gioui.org/internal/scene"
|
||||
)
|
||||
|
||||
// The following are copies of types from op/clip to avoid a circular import of
|
||||
// that package.
|
||||
// TODO: when the old renderer is gone, this package can be merged with
|
||||
// op/clip, eliminating the duplicate types.
|
||||
type StrokeStyle struct {
|
||||
Width float32
|
||||
}
|
||||
|
||||
// strokeTolerance is used to reconcile rounding errors arising
|
||||
// when splitting quads into smaller and smaller segments to approximate
|
||||
// them into straight lines, and when joining back segments.
|
||||
//
|
||||
// The magic value of 0.01 was found by striking a compromise between
|
||||
// aesthetic looking (curves did look like curves, even after linearization)
|
||||
// and speed.
|
||||
const strokeTolerance = 0.01
|
||||
|
||||
type QuadSegment struct {
|
||||
From, Ctrl, To f32.Point
|
||||
}
|
||||
|
||||
type StrokeQuad struct {
|
||||
Contour uint32
|
||||
Quad QuadSegment
|
||||
}
|
||||
|
||||
type strokeState struct {
|
||||
p0, p1 f32.Point // p0 is the start point, p1 the end point.
|
||||
n0, n1 f32.Point // n0 is the normal vector at the start point, n1 at the end point.
|
||||
r0, r1 float32 // r0 is the curvature at the start point, r1 at the end point.
|
||||
ctl f32.Point // ctl is the control point of the quadratic Bézier segment.
|
||||
}
|
||||
|
||||
type StrokeQuads []StrokeQuad
|
||||
|
||||
func (qs *StrokeQuads) pen() f32.Point {
|
||||
return (*qs)[len(*qs)-1].Quad.To
|
||||
}
|
||||
|
||||
func (qs *StrokeQuads) lineTo(pt f32.Point) {
|
||||
end := qs.pen()
|
||||
*qs = append(*qs, StrokeQuad{
|
||||
Quad: QuadSegment{
|
||||
From: end,
|
||||
Ctrl: end.Add(pt).Mul(0.5),
|
||||
To: pt,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func (qs *StrokeQuads) arc(f1, f2 f32.Point, angle float32) {
|
||||
pen := qs.pen()
|
||||
m, segments := ArcTransform(pen, f1.Add(pen), f2.Add(pen), angle)
|
||||
for range segments {
|
||||
p0 := qs.pen()
|
||||
p1 := m.Transform(p0)
|
||||
p2 := m.Transform(p1)
|
||||
ctl := p1.Mul(2).Sub(p0.Add(p2).Mul(.5))
|
||||
*qs = append(*qs, StrokeQuad{
|
||||
Quad: QuadSegment{
|
||||
From: p0, Ctrl: ctl, To: p2,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// split splits a slice of quads into slices of quads grouped
|
||||
// by contours (ie: splitted at move-to boundaries).
|
||||
func (qs StrokeQuads) split() []StrokeQuads {
|
||||
if len(qs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
c uint32
|
||||
o []StrokeQuads
|
||||
i = len(o)
|
||||
)
|
||||
for _, q := range qs {
|
||||
if q.Contour != c {
|
||||
c = q.Contour
|
||||
i = len(o)
|
||||
o = append(o, StrokeQuads{})
|
||||
}
|
||||
o[i] = append(o[i], q)
|
||||
}
|
||||
|
||||
return o
|
||||
}
|
||||
|
||||
func (qs StrokeQuads) stroke(stroke StrokeStyle) StrokeQuads {
|
||||
var (
|
||||
o StrokeQuads
|
||||
hw = 0.5 * stroke.Width
|
||||
)
|
||||
|
||||
for _, ps := range qs.split() {
|
||||
rhs, lhs := ps.offset(hw, stroke)
|
||||
switch lhs {
|
||||
case nil:
|
||||
o = o.append(rhs)
|
||||
default:
|
||||
// Closed path.
|
||||
// Inner path should go opposite direction to cancel outer path.
|
||||
switch {
|
||||
case ps.ccw():
|
||||
lhs = lhs.reverse()
|
||||
o = o.append(rhs)
|
||||
o = o.append(lhs)
|
||||
default:
|
||||
rhs = rhs.reverse()
|
||||
o = o.append(lhs)
|
||||
o = o.append(rhs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return o
|
||||
}
|
||||
|
||||
// offset returns the right-hand and left-hand sides of the path, offset by
|
||||
// the half-width hw.
|
||||
// The stroke handles how segments are joined and ends are capped.
|
||||
func (qs StrokeQuads) offset(hw float32, stroke StrokeStyle) (rhs, lhs StrokeQuads) {
|
||||
var (
|
||||
states []strokeState
|
||||
beg = qs[0].Quad.From
|
||||
end = qs[len(qs)-1].Quad.To
|
||||
closed = beg == end
|
||||
)
|
||||
for i := range qs {
|
||||
q := qs[i].Quad
|
||||
|
||||
var (
|
||||
n0 = strokePathNorm(q.From, q.Ctrl, q.To, 0, hw)
|
||||
n1 = strokePathNorm(q.From, q.Ctrl, q.To, 1, hw)
|
||||
r0 = strokePathCurv(q.From, q.Ctrl, q.To, 0)
|
||||
r1 = strokePathCurv(q.From, q.Ctrl, q.To, 1)
|
||||
)
|
||||
states = append(states, strokeState{
|
||||
p0: q.From,
|
||||
p1: q.To,
|
||||
n0: n0,
|
||||
n1: n1,
|
||||
r0: r0,
|
||||
r1: r1,
|
||||
ctl: q.Ctrl,
|
||||
})
|
||||
}
|
||||
|
||||
for i, state := range states {
|
||||
rhs = rhs.append(strokeQuadBezier(state, +hw, strokeTolerance))
|
||||
lhs = lhs.append(strokeQuadBezier(state, -hw, strokeTolerance))
|
||||
|
||||
// join the current and next segments
|
||||
if hasNext := i+1 < len(states); hasNext || closed {
|
||||
var next strokeState
|
||||
switch {
|
||||
case hasNext:
|
||||
next = states[i+1]
|
||||
case closed:
|
||||
next = states[0]
|
||||
}
|
||||
if state.n1 != next.n0 {
|
||||
strokePathRoundJoin(&rhs, &lhs, hw, state.p1, state.n1, next.n0, state.r1, next.r0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if closed {
|
||||
rhs.close()
|
||||
lhs.close()
|
||||
return rhs, lhs
|
||||
}
|
||||
|
||||
qbeg := &states[0]
|
||||
qend := &states[len(states)-1]
|
||||
|
||||
// Default to counter-clockwise direction.
|
||||
lhs = lhs.reverse()
|
||||
strokePathCap(stroke, &rhs, hw, qend.p1, qend.n1)
|
||||
|
||||
rhs = rhs.append(lhs)
|
||||
strokePathCap(stroke, &rhs, hw, qbeg.p0, qbeg.n0.Mul(-1))
|
||||
|
||||
rhs.close()
|
||||
|
||||
return rhs, nil
|
||||
}
|
||||
|
||||
func (qs *StrokeQuads) close() {
|
||||
p0 := (*qs)[len(*qs)-1].Quad.To
|
||||
p1 := (*qs)[0].Quad.From
|
||||
|
||||
if p1 == p0 {
|
||||
return
|
||||
}
|
||||
|
||||
*qs = append(*qs, StrokeQuad{
|
||||
Quad: QuadSegment{
|
||||
From: p0,
|
||||
Ctrl: p0.Add(p1).Mul(0.5),
|
||||
To: p1,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// ccw returns whether the path is counter-clockwise.
|
||||
func (qs StrokeQuads) ccw() bool {
|
||||
// Use the Shoelace formula:
|
||||
// https://en.wikipedia.org/wiki/Shoelace_formula
|
||||
var area float32
|
||||
for _, ps := range qs.split() {
|
||||
for i := 1; i < len(ps); i++ {
|
||||
pi := ps[i].Quad.To
|
||||
pj := ps[i-1].Quad.To
|
||||
area += (pi.X - pj.X) * (pi.Y + pj.Y)
|
||||
}
|
||||
}
|
||||
return area <= 0.0
|
||||
}
|
||||
|
||||
func (qs StrokeQuads) reverse() StrokeQuads {
|
||||
if len(qs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
ps := make(StrokeQuads, 0, len(qs))
|
||||
for i := range qs {
|
||||
q := qs[len(qs)-1-i]
|
||||
q.Quad.To, q.Quad.From = q.Quad.From, q.Quad.To
|
||||
ps = append(ps, q)
|
||||
}
|
||||
|
||||
return ps
|
||||
}
|
||||
|
||||
func (qs StrokeQuads) append(ps StrokeQuads) StrokeQuads {
|
||||
switch {
|
||||
case len(ps) == 0:
|
||||
return qs
|
||||
case len(qs) == 0:
|
||||
return ps
|
||||
}
|
||||
|
||||
// Consolidate quads and smooth out rounding errors.
|
||||
// We need to also check for the strokeTolerance to correctly handle
|
||||
// join/cap points or on-purpose disjoint quads.
|
||||
p0 := qs[len(qs)-1].Quad.To
|
||||
p1 := ps[0].Quad.From
|
||||
if p0 != p1 && lenPt(p0.Sub(p1)) < strokeTolerance {
|
||||
qs = append(qs, StrokeQuad{
|
||||
Quad: QuadSegment{
|
||||
From: p0,
|
||||
Ctrl: p0.Add(p1).Mul(0.5),
|
||||
To: p1,
|
||||
},
|
||||
})
|
||||
}
|
||||
return append(qs, ps...)
|
||||
}
|
||||
|
||||
func (q QuadSegment) Transform(t f32.Affine2D) QuadSegment {
|
||||
q.From = t.Transform(q.From)
|
||||
q.Ctrl = t.Transform(q.Ctrl)
|
||||
q.To = t.Transform(q.To)
|
||||
return q
|
||||
}
|
||||
|
||||
// strokePathNorm returns the normal vector at t.
|
||||
func strokePathNorm(p0, p1, p2 f32.Point, t, d float32) f32.Point {
|
||||
switch t {
|
||||
case 0:
|
||||
n := p1.Sub(p0)
|
||||
if n.X == 0 && n.Y == 0 {
|
||||
return f32.Point{}
|
||||
}
|
||||
n = rot90CW(n)
|
||||
return normPt(n, d)
|
||||
case 1:
|
||||
n := p2.Sub(p1)
|
||||
if n.X == 0 && n.Y == 0 {
|
||||
return f32.Point{}
|
||||
}
|
||||
n = rot90CW(n)
|
||||
return normPt(n, d)
|
||||
}
|
||||
panic("impossible")
|
||||
}
|
||||
|
||||
func rot90CW(p f32.Point) f32.Point { return f32.Pt(+p.Y, -p.X) }
|
||||
|
||||
func normPt(p f32.Point, l float32) f32.Point {
|
||||
if (p.X == 0 && p.Y == 0) || l == 0 {
|
||||
return f32.Point{}
|
||||
}
|
||||
isVerticalUnit := p.X == 0 && (p.Y == l || p.Y == -l)
|
||||
isHorizontalUnit := p.Y == 0 && (p.X == l || p.X == -l)
|
||||
if isVerticalUnit || isHorizontalUnit {
|
||||
if math.Signbit(float64(l)) {
|
||||
return f32.Point{X: -p.X, Y: -p.Y}
|
||||
} else {
|
||||
return f32.Point{X: p.X, Y: p.Y}
|
||||
}
|
||||
}
|
||||
d := math.Hypot(float64(p.X), float64(p.Y))
|
||||
l64 := float64(l)
|
||||
if math.Abs(d-l64) < 1e-10 {
|
||||
if math.Signbit(float64(l)) {
|
||||
return f32.Point{X: -p.X, Y: -p.Y}
|
||||
} else {
|
||||
return f32.Point{X: p.X, Y: p.Y}
|
||||
}
|
||||
}
|
||||
n := float32(l64 / d)
|
||||
return f32.Point{X: p.X * n, Y: p.Y * n}
|
||||
}
|
||||
|
||||
func lenPt(p f32.Point) float32 {
|
||||
return float32(math.Hypot(float64(p.X), float64(p.Y)))
|
||||
}
|
||||
|
||||
func perpDot(p, q f32.Point) float32 {
|
||||
return p.X*q.Y - p.Y*q.X
|
||||
}
|
||||
|
||||
func angleBetween(n0, n1 f32.Point) float64 {
|
||||
return math.Atan2(float64(n1.Y), float64(n1.X)) -
|
||||
math.Atan2(float64(n0.Y), float64(n0.X))
|
||||
}
|
||||
|
||||
// strokePathCurv returns the curvature at t, along the quadratic Bézier
|
||||
// curve defined by the triplet (beg, ctl, end).
|
||||
func strokePathCurv(beg, ctl, end f32.Point, t float32) float32 {
|
||||
var (
|
||||
d1p = quadBezierD1(beg, ctl, end, t)
|
||||
d2p = quadBezierD2(beg, ctl, end, t)
|
||||
|
||||
// Negative when bending right, ie: the curve is CW at this point.
|
||||
a = float64(perpDot(d1p, d2p))
|
||||
)
|
||||
|
||||
// We check early that the segment isn't too line-like and
|
||||
// save a costly call to math.Pow that will be discarded by dividing
|
||||
// with a too small 'a'.
|
||||
if math.Abs(a) < 1e-10 {
|
||||
return float32(math.NaN())
|
||||
}
|
||||
return float32(math.Pow(float64(d1p.X*d1p.X+d1p.Y*d1p.Y), 1.5) / a)
|
||||
}
|
||||
|
||||
// quadBezierSample returns the point on the Bézier curve at t.
|
||||
//
|
||||
// B(t) = (1-t)^2 P0 + 2(1-t)t P1 + t^2 P2
|
||||
func quadBezierSample(p0, p1, p2 f32.Point, t float32) f32.Point {
|
||||
t1 := 1 - t
|
||||
c0 := t1 * t1
|
||||
c1 := 2 * t1 * t
|
||||
c2 := t * t
|
||||
|
||||
o := p0.Mul(c0)
|
||||
o = o.Add(p1.Mul(c1))
|
||||
o = o.Add(p2.Mul(c2))
|
||||
return o
|
||||
}
|
||||
|
||||
// quadBezierD1 returns the first derivative of the Bézier curve with respect to t.
|
||||
//
|
||||
// B'(t) = 2(1-t)(P1 - P0) + 2t(P2 - P1)
|
||||
func quadBezierD1(p0, p1, p2 f32.Point, t float32) f32.Point {
|
||||
p10 := p1.Sub(p0).Mul(2 * (1 - t))
|
||||
p21 := p2.Sub(p1).Mul(2 * t)
|
||||
|
||||
return p10.Add(p21)
|
||||
}
|
||||
|
||||
// quadBezierD2 returns the second derivative of the Bézier curve with respect to t:
|
||||
//
|
||||
// B''(t) = 2(P2 - 2P1 + P0)
|
||||
func quadBezierD2(p0, p1, p2 f32.Point, t float32) f32.Point {
|
||||
p := p2.Sub(p1.Mul(2)).Add(p0)
|
||||
return p.Mul(2)
|
||||
}
|
||||
|
||||
func strokeQuadBezier(state strokeState, d, flatness float32) StrokeQuads {
|
||||
// Gio strokes are only quadratic Bézier curves, w/o any inflection point.
|
||||
// So we just have to flatten them.
|
||||
var qs StrokeQuads
|
||||
return flattenQuadBezier(qs, state.p0, state.ctl, state.p1, d, flatness)
|
||||
}
|
||||
|
||||
// flattenQuadBezier splits a Bézier quadratic curve into linear sub-segments,
|
||||
// themselves also encoded as Bézier (degenerate, flat) quadratic curves.
|
||||
func flattenQuadBezier(qs StrokeQuads, p0, p1, p2 f32.Point, d, flatness float32) StrokeQuads {
|
||||
var (
|
||||
t float32
|
||||
flat64 = float64(flatness)
|
||||
)
|
||||
for t < 1 {
|
||||
s2 := float64((p2.X-p0.X)*(p1.Y-p0.Y) - (p2.Y-p0.Y)*(p1.X-p0.X))
|
||||
den := math.Hypot(float64(p1.X-p0.X), float64(p1.Y-p0.Y))
|
||||
if s2*den == 0.0 {
|
||||
break
|
||||
}
|
||||
|
||||
s2 /= den
|
||||
t = 2.0 * float32(math.Sqrt(flat64/3.0/math.Abs(s2)))
|
||||
if t >= 1.0 {
|
||||
break
|
||||
}
|
||||
var q0, q1, q2 f32.Point
|
||||
q0, q1, q2, p0, p1, p2 = quadBezierSplit(p0, p1, p2, t)
|
||||
qs.addLine(q0, q1, q2, 0, d)
|
||||
}
|
||||
qs.addLine(p0, p1, p2, 1, d)
|
||||
return qs
|
||||
}
|
||||
|
||||
func (qs *StrokeQuads) addLine(p0, ctrl, p1 f32.Point, t, d float32) {
|
||||
switch i := len(*qs); i {
|
||||
case 0:
|
||||
p0 = p0.Add(strokePathNorm(p0, ctrl, p1, 0, d))
|
||||
default:
|
||||
// Address possible rounding errors and use previous point.
|
||||
p0 = (*qs)[i-1].Quad.To
|
||||
}
|
||||
|
||||
p1 = p1.Add(strokePathNorm(p0, ctrl, p1, 1, d))
|
||||
|
||||
*qs = append(*qs,
|
||||
StrokeQuad{
|
||||
Quad: QuadSegment{
|
||||
From: p0,
|
||||
Ctrl: p0.Add(p1).Mul(0.5),
|
||||
To: p1,
|
||||
},
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// quadInterp returns the interpolated point at t.
|
||||
func quadInterp(p, q f32.Point, t float32) f32.Point {
|
||||
return f32.Pt(
|
||||
(1-t)*p.X+t*q.X,
|
||||
(1-t)*p.Y+t*q.Y,
|
||||
)
|
||||
}
|
||||
|
||||
// quadBezierSplit returns the pair of triplets (from,ctrl,to) Bézier curve,
|
||||
// split before (resp. after) the provided parametric t value.
|
||||
func quadBezierSplit(p0, p1, p2 f32.Point, t float32) (f32.Point, f32.Point, f32.Point, f32.Point, f32.Point, f32.Point) {
|
||||
var (
|
||||
b0 = p0
|
||||
b1 = quadInterp(p0, p1, t)
|
||||
b2 = quadBezierSample(p0, p1, p2, t)
|
||||
|
||||
a0 = b2
|
||||
a1 = quadInterp(p1, p2, t)
|
||||
a2 = p2
|
||||
)
|
||||
|
||||
return b0, b1, b2, a0, a1, a2
|
||||
}
|
||||
|
||||
// strokePathRoundJoin joins the two paths rhs and lhs, creating an arc.
|
||||
func strokePathRoundJoin(rhs, lhs *StrokeQuads, hw float32, pivot, n0, n1 f32.Point, r0, r1 float32) {
|
||||
rp := pivot.Add(n1)
|
||||
lp := pivot.Sub(n1)
|
||||
angle := angleBetween(n0, n1)
|
||||
switch {
|
||||
case angle <= 0:
|
||||
// Path bends to the right, ie. CW (or 180 degree turn).
|
||||
c := pivot.Sub(lhs.pen())
|
||||
lhs.arc(c, c, float32(angle))
|
||||
lhs.lineTo(lp) // Add a line to accommodate for rounding errors.
|
||||
rhs.lineTo(rp)
|
||||
default:
|
||||
// Path bends to the left, ie. CCW.
|
||||
c := pivot.Sub(rhs.pen())
|
||||
rhs.arc(c, c, float32(angle))
|
||||
rhs.lineTo(rp) // Add a line to accommodate for rounding errors.
|
||||
lhs.lineTo(lp)
|
||||
}
|
||||
}
|
||||
|
||||
// strokePathCap caps the provided path qs, according to the provided stroke operation.
|
||||
func strokePathCap(stroke StrokeStyle, qs *StrokeQuads, hw float32, pivot, n0 f32.Point) {
|
||||
strokePathRoundCap(qs, hw, pivot, n0)
|
||||
}
|
||||
|
||||
// strokePathRoundCap caps the start or end of a path with a round cap.
|
||||
func strokePathRoundCap(qs *StrokeQuads, hw float32, pivot, n0 f32.Point) {
|
||||
c := pivot.Sub(qs.pen())
|
||||
qs.arc(c, c, math.Pi)
|
||||
}
|
||||
|
||||
// ArcTransform computes a transformation that can be used for generating quadratic bézier
|
||||
// curve approximations for an arc.
|
||||
//
|
||||
// The math is extracted from the following paper:
|
||||
//
|
||||
// "Drawing an elliptical arc using polylines, quadratic or
|
||||
// cubic Bezier curves", L. Maisonobe
|
||||
//
|
||||
// An electronic version may be found at:
|
||||
//
|
||||
// http://spaceroots.org/documents/ellipse/elliptical-arc.pdf
|
||||
func ArcTransform(p, f1, f2 f32.Point, angle float32) (transform f32.Affine2D, segments int) {
|
||||
const segmentsPerCircle = 16
|
||||
const anglePerSegment = 2 * math.Pi / segmentsPerCircle
|
||||
|
||||
s := angle / anglePerSegment
|
||||
if s < 0 {
|
||||
s = -s
|
||||
}
|
||||
segments = int(math.Ceil(float64(s)))
|
||||
if segments <= 0 {
|
||||
segments = 1
|
||||
}
|
||||
|
||||
var rx, ry, alpha float64
|
||||
if f1 == f2 {
|
||||
// degenerate case of a circle.
|
||||
rx = dist(f1, p)
|
||||
ry = rx
|
||||
} else {
|
||||
// semi-major axis: 2a = |PF1| + |PF2|
|
||||
a := 0.5 * (dist(f1, p) + dist(f2, p))
|
||||
// semi-minor axis: c^2 = a^2 - b^2 (c: focal distance)
|
||||
c := dist(f1, f2) * 0.5
|
||||
b := math.Sqrt(a*a - c*c)
|
||||
switch {
|
||||
case a > b:
|
||||
rx = a
|
||||
ry = b
|
||||
default:
|
||||
rx = b
|
||||
ry = a
|
||||
}
|
||||
if f1.X == f2.X {
|
||||
// special case of a "vertical" ellipse.
|
||||
alpha = math.Pi / 2
|
||||
if f1.Y < f2.Y {
|
||||
alpha = -alpha
|
||||
}
|
||||
} else {
|
||||
x := float64(f1.X-f2.X) * 0.5
|
||||
if x < 0 {
|
||||
x = -x
|
||||
}
|
||||
alpha = math.Acos(x / c)
|
||||
}
|
||||
}
|
||||
|
||||
θ := angle / float32(segments)
|
||||
ref := f32.AffineId() // transform from absolute frame to ellipse-based one
|
||||
rot := f32.AffineId() // rotation matrix for each segment
|
||||
inv := f32.AffineId() // transform from ellipse-based frame to absolute one
|
||||
center := f32.Point{
|
||||
X: 0.5 * (f1.X + f2.X),
|
||||
Y: 0.5 * (f1.Y + f2.Y),
|
||||
}
|
||||
ref = ref.Offset(f32.Point{}.Sub(center))
|
||||
ref = ref.Rotate(f32.Point{}, float32(-alpha))
|
||||
ref = ref.Scale(f32.Point{}, f32.Point{
|
||||
X: float32(1 / rx),
|
||||
Y: float32(1 / ry),
|
||||
})
|
||||
inv = ref.Invert()
|
||||
rot = rot.Rotate(f32.Point{}, 0.5*θ)
|
||||
|
||||
// Instead of invoking math.Sincos for every segment, compute a rotation
|
||||
// matrix once and apply for each segment.
|
||||
// Before applying the rotation matrix rot, transform the coordinates
|
||||
// to a frame centered to the ellipse (and warped into a unit circle), then rotate.
|
||||
// Finally, transform back into the original frame.
|
||||
return inv.Mul(rot).Mul(ref), segments
|
||||
}
|
||||
|
||||
func dist(p1, p2 f32.Point) float64 {
|
||||
var (
|
||||
x1 = float64(p1.X)
|
||||
y1 = float64(p1.Y)
|
||||
x2 = float64(p2.X)
|
||||
y2 = float64(p2.Y)
|
||||
dx = x2 - x1
|
||||
dy = y2 - y1
|
||||
)
|
||||
return math.Hypot(dx, dy)
|
||||
}
|
||||
|
||||
func StrokePathCommands(style StrokeStyle, scene []byte) StrokeQuads {
|
||||
quads := decodeToStrokeQuads(scene)
|
||||
return quads.stroke(style)
|
||||
}
|
||||
|
||||
// decodeToStrokeQuads decodes scene commands to quads ready to stroke.
|
||||
func decodeToStrokeQuads(pathData []byte) StrokeQuads {
|
||||
quads := make(StrokeQuads, 0, 2*len(pathData)/(scene.CommandSize+4))
|
||||
scratch := make([]QuadSegment, 0, 10)
|
||||
for len(pathData) >= scene.CommandSize+4 {
|
||||
contour := binary.LittleEndian.Uint32(pathData)
|
||||
cmd := ops.DecodeCommand(pathData[4:])
|
||||
switch cmd.Op() {
|
||||
case scene.OpLine:
|
||||
var q QuadSegment
|
||||
q.From, q.To = scene.DecodeLine(cmd)
|
||||
q.Ctrl = q.From.Add(q.To).Mul(.5)
|
||||
quad := StrokeQuad{
|
||||
Contour: contour,
|
||||
Quad: q,
|
||||
}
|
||||
quads = append(quads, quad)
|
||||
case scene.OpGap:
|
||||
// Ignore gaps for strokes.
|
||||
case scene.OpQuad:
|
||||
var q QuadSegment
|
||||
q.From, q.Ctrl, q.To = scene.DecodeQuad(cmd)
|
||||
quad := StrokeQuad{
|
||||
Contour: contour,
|
||||
Quad: q,
|
||||
}
|
||||
quads = append(quads, quad)
|
||||
case scene.OpCubic:
|
||||
from, ctrl0, ctrl1, to := scene.DecodeCubic(cmd)
|
||||
scratch = SplitCubic(from, ctrl0, ctrl1, to, scratch[:0])
|
||||
for _, q := range scratch {
|
||||
quad := StrokeQuad{
|
||||
Contour: contour,
|
||||
Quad: q,
|
||||
}
|
||||
quads = append(quads, quad)
|
||||
}
|
||||
default:
|
||||
panic("unsupported scene command")
|
||||
}
|
||||
pathData = pathData[scene.CommandSize+4:]
|
||||
}
|
||||
return quads
|
||||
}
|
||||
|
||||
func SplitCubic(from, ctrl0, ctrl1, to f32.Point, quads []QuadSegment) []QuadSegment {
|
||||
// Set the maximum distance proportionally to the longest side
|
||||
// of the bounding rectangle.
|
||||
hull := f32.Rectangle{
|
||||
Min: from,
|
||||
Max: ctrl0,
|
||||
}.Canon().Union(f32.Rectangle{
|
||||
Min: ctrl1,
|
||||
Max: to,
|
||||
}.Canon())
|
||||
l := hull.Dx()
|
||||
if h := hull.Dy(); h > l {
|
||||
l = h
|
||||
}
|
||||
maxDist := l * 0.001
|
||||
approxCubeTo(&quads, 0, maxDist*maxDist, from, ctrl0, ctrl1, to)
|
||||
return quads
|
||||
}
|
||||
|
||||
// approxCubeTo approximates a cubic Bézier by a series of quadratic
|
||||
// curves.
|
||||
func approxCubeTo(quads *[]QuadSegment, splits int, maxDistSq float32, from, ctrl0, ctrl1, to f32.Point) int {
|
||||
// The idea is from
|
||||
// https://caffeineowl.com/graphics/2d/vectorial/cubic2quad01.html
|
||||
// where a quadratic approximates a cubic by eliminating its t³ term
|
||||
// from its polynomial expression anchored at the starting point:
|
||||
//
|
||||
// P(t) = pen + 3t(ctrl0 - pen) + 3t²(ctrl1 - 2ctrl0 + pen) + t³(to - 3ctrl1 + 3ctrl0 - pen)
|
||||
//
|
||||
// The control point for the new quadratic Q1 that shares starting point, pen, with P is
|
||||
//
|
||||
// C1 = (3ctrl0 - pen)/2
|
||||
//
|
||||
// The reverse cubic anchored at the end point has the polynomial
|
||||
//
|
||||
// P'(t) = to + 3t(ctrl1 - to) + 3t²(ctrl0 - 2ctrl1 + to) + t³(pen - 3ctrl0 + 3ctrl1 - to)
|
||||
//
|
||||
// The corresponding quadratic Q2 that shares the end point, to, with P has control
|
||||
// point
|
||||
//
|
||||
// C2 = (3ctrl1 - to)/2
|
||||
//
|
||||
// The combined quadratic Bézier, Q, shares both start and end points with its cubic
|
||||
// and use the midpoint between the two curves Q1 and Q2 as control point:
|
||||
//
|
||||
// C = (3ctrl0 - pen + 3ctrl1 - to)/4
|
||||
// using, q0 := 3ctrl0 - pen, q1 := 3ctrl1 - to
|
||||
// C = (q0 + q1)/4
|
||||
q0 := ctrl0.Mul(3).Sub(from)
|
||||
q1 := ctrl1.Mul(3).Sub(to)
|
||||
c := q0.Add(q1).Mul(1.0 / 4.0)
|
||||
const maxSplits = 32
|
||||
if splits >= maxSplits {
|
||||
*quads = append(*quads, QuadSegment{From: from, Ctrl: c, To: to})
|
||||
return splits
|
||||
}
|
||||
// The maximum distance between the cubic P and its approximation Q given t
|
||||
// can be shown to be
|
||||
//
|
||||
// d = sqrt(3)/36 * |to - 3ctrl1 + 3ctrl0 - pen|
|
||||
// reusing, q0 := 3ctrl0 - pen, q1 := 3ctrl1 - to
|
||||
// d = sqrt(3)/36 * |-q1 + q0|
|
||||
//
|
||||
// To save a square root, compare d² with the squared tolerance.
|
||||
v := q0.Sub(q1)
|
||||
d2 := (v.X*v.X + v.Y*v.Y) * 3 / (36 * 36)
|
||||
if d2 <= maxDistSq {
|
||||
*quads = append(*quads, QuadSegment{From: from, Ctrl: c, To: to})
|
||||
return splits
|
||||
}
|
||||
// De Casteljau split the curve and approximate the halves.
|
||||
t := float32(0.5)
|
||||
c0 := from.Add(ctrl0.Sub(from).Mul(t))
|
||||
c1 := ctrl0.Add(ctrl1.Sub(ctrl0).Mul(t))
|
||||
c2 := ctrl1.Add(to.Sub(ctrl1).Mul(t))
|
||||
c01 := c0.Add(c1.Sub(c0).Mul(t))
|
||||
c12 := c1.Add(c2.Sub(c1).Mul(t))
|
||||
c0112 := c01.Add(c12.Sub(c01).Mul(t))
|
||||
splits++
|
||||
splits = approxCubeTo(quads, splits, maxDistSq, from, c0, c01, c0112)
|
||||
splits = approxCubeTo(quads, splits, maxDistSq, c0112, c12, c2, to)
|
||||
return splits
|
||||
}
|
||||
+2145
File diff suppressed because it is too large
Load Diff
+46
@@ -0,0 +1,46 @@
|
||||
// SPDX-License-Identifier: Unlicense OR MIT
|
||||
|
||||
//go:build !nowayland
|
||||
// +build !nowayland
|
||||
|
||||
package vk
|
||||
|
||||
/*
|
||||
#define VK_USE_PLATFORM_ANDROID_KHR
|
||||
#define VK_NO_PROTOTYPES 1
|
||||
#define VK_DEFINE_NON_DISPATCHABLE_HANDLE(object) typedef uint64_t object;
|
||||
#include <android/native_window.h>
|
||||
#include <vulkan/vulkan.h>
|
||||
|
||||
static VkResult vkCreateAndroidSurfaceKHR(PFN_vkCreateAndroidSurfaceKHR f, VkInstance instance, const VkAndroidSurfaceCreateInfoKHR *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkSurfaceKHR *pSurface) {
|
||||
return f(instance, pCreateInfo, pAllocator, pSurface);
|
||||
}
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var wlFuncs struct {
|
||||
vkCreateAndroidSurfaceKHR C.PFN_vkCreateAndroidSurfaceKHR
|
||||
}
|
||||
|
||||
func init() {
|
||||
loadFuncs = append(loadFuncs, func(dlopen func(name string) *[0]byte) {
|
||||
wlFuncs.vkCreateAndroidSurfaceKHR = dlopen("vkCreateAndroidSurfaceKHR")
|
||||
})
|
||||
}
|
||||
|
||||
func CreateAndroidSurface(inst Instance, window unsafe.Pointer) (Surface, error) {
|
||||
inf := C.VkAndroidSurfaceCreateInfoKHR{
|
||||
sType: C.VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR,
|
||||
window: (*C.ANativeWindow)(window),
|
||||
}
|
||||
var surf Surface
|
||||
if err := vkErr(C.vkCreateAndroidSurfaceKHR(wlFuncs.vkCreateAndroidSurfaceKHR, inst, &inf, nil, &surf)); err != nil {
|
||||
return 0, fmt.Errorf("vulkan: vkCreateAndroidSurfaceKHR: %w", err)
|
||||
}
|
||||
return surf, nil
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
// SPDX-License-Identifier: Unlicense OR MIT
|
||||
|
||||
//go:build ((linux && !android) || freebsd) && !nowayland
|
||||
// +build linux,!android freebsd
|
||||
// +build !nowayland
|
||||
|
||||
package vk
|
||||
|
||||
/*
|
||||
#cgo linux pkg-config: wayland-client
|
||||
|
||||
#define VK_USE_PLATFORM_WAYLAND_KHR
|
||||
#define VK_NO_PROTOTYPES 1
|
||||
#define VK_DEFINE_NON_DISPATCHABLE_HANDLE(object) typedef uint64_t object;
|
||||
#include <vulkan/vulkan.h>
|
||||
|
||||
static VkResult vkCreateWaylandSurfaceKHR(PFN_vkCreateWaylandSurfaceKHR f, VkInstance instance, const VkWaylandSurfaceCreateInfoKHR *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkSurfaceKHR *pSurface) {
|
||||
return f(instance, pCreateInfo, pAllocator, pSurface);
|
||||
}
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var wlFuncs struct {
|
||||
vkCreateWaylandSurfaceKHR C.PFN_vkCreateWaylandSurfaceKHR
|
||||
}
|
||||
|
||||
func init() {
|
||||
loadFuncs = append(loadFuncs, func(dlopen func(name string) *[0]byte) {
|
||||
wlFuncs.vkCreateWaylandSurfaceKHR = dlopen("vkCreateWaylandSurfaceKHR")
|
||||
})
|
||||
}
|
||||
|
||||
func CreateWaylandSurface(inst Instance, disp unsafe.Pointer, wlSurf unsafe.Pointer) (Surface, error) {
|
||||
inf := C.VkWaylandSurfaceCreateInfoKHR{
|
||||
sType: C.VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR,
|
||||
display: (*C.struct_wl_display)(disp),
|
||||
surface: (*C.struct_wl_surface)(wlSurf),
|
||||
}
|
||||
var surf Surface
|
||||
if err := vkErr(C.vkCreateWaylandSurfaceKHR(wlFuncs.vkCreateWaylandSurfaceKHR, inst, &inf, nil, &surf)); err != nil {
|
||||
return 0, fmt.Errorf("vulkan: vkCreateWaylandSurfaceKHR: %w", err)
|
||||
}
|
||||
return surf, nil
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
// SPDX-License-Identifier: Unlicense OR MIT
|
||||
|
||||
//go:build ((linux && !android) || freebsd) && !nox11
|
||||
// +build linux,!android freebsd
|
||||
// +build !nox11
|
||||
|
||||
package vk
|
||||
|
||||
/*
|
||||
#define VK_USE_PLATFORM_XLIB_KHR
|
||||
#define VK_NO_PROTOTYPES 1
|
||||
#define VK_DEFINE_NON_DISPATCHABLE_HANDLE(object) typedef uint64_t object;
|
||||
#include <vulkan/vulkan.h>
|
||||
|
||||
static VkResult vkCreateXlibSurfaceKHR(PFN_vkCreateXlibSurfaceKHR f, VkInstance instance, const VkXlibSurfaceCreateInfoKHR *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkSurfaceKHR *pSurface) {
|
||||
return f(instance, pCreateInfo, pAllocator, pSurface);
|
||||
}
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var x11Funcs struct {
|
||||
vkCreateXlibSurfaceKHR C.PFN_vkCreateXlibSurfaceKHR
|
||||
}
|
||||
|
||||
func init() {
|
||||
loadFuncs = append(loadFuncs, func(dlopen func(name string) *[0]byte) {
|
||||
x11Funcs.vkCreateXlibSurfaceKHR = dlopen("vkCreateXlibSurfaceKHR")
|
||||
})
|
||||
}
|
||||
|
||||
func CreateXlibSurface(inst Instance, dpy unsafe.Pointer, window uintptr) (Surface, error) {
|
||||
inf := C.VkXlibSurfaceCreateInfoKHR{
|
||||
sType: C.VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR,
|
||||
dpy: (*C.Display)(dpy),
|
||||
window: (C.Window)(window),
|
||||
}
|
||||
var surf Surface
|
||||
if err := vkErr(C.vkCreateXlibSurfaceKHR(x11Funcs.vkCreateXlibSurfaceKHR, inst, &inf, nil, &surf)); err != nil {
|
||||
return 0, fmt.Errorf("vulkan: vkCreateXlibSurfaceKHR: %w", err)
|
||||
}
|
||||
return surf, nil
|
||||
}
|
||||
Reference in New Issue
Block a user