49 Commits

Author SHA1 Message Date
Elias Naur 3f4f8ba7c1 app: [macOS] make the app delegate optional
Inspired by the discussion at golang.org/issue/70089, this change makes
our particular NSApplicationDelegate implementation optional.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2025-09-10 23:04:24 +02:00
Elias Naur 6553915e59 app: [macOS/iOS] simplify running functions in the main thread
Using cgo.Handle allows us to pass a reference to a Go function through
the GCD API for running main thread code, saving a goroutine and a channel.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2025-09-10 22:55:26 +02:00
Walter Werner SCHNEIDER 4c0e526c0b gpu/clip: fix vertex corner positions
Fixes the 1px overlap of curve quads. Without this patch the rendered quads
were skewed vertically and were 2 pixels shorter in height. The NorthWest
pixels were moved to the SouthWest instead of NorthWest and the SouthEast
pixels were moved to the NorthEast instead of SouthEast.

References: https://todo.sr.ht/~eliasnaur/gio/339
Signed-off-by: Walter Werner SCHNEIDER <contact@schnwalter.eu>
Signed-off-by: Elias Naur <mail@eliasnaur.com>
2025-08-01 21:15:33 +02:00
Walter Werner SCHNEIDER f45039734f internal/stroke: quickly handle zero length normalization
Signed-off-by: Walter Werner SCHNEIDER <contact@schnwalter.eu>
2025-07-28 21:28:31 +02:00
Walter Werner SCHNEIDER 30f8ac10b7 internal/stroke: fix point normalization for unit length.
This fixes the cases where the unit length matches the hypotenuse.

Signed-off-by: Walter Werner SCHNEIDER <contact@schnwalter.eu>
2025-07-28 21:28:26 +02:00
Walter Werner SCHNEIDER 176570527d internal/stroke: handle zero-length points
Fixes the edge case where a zero point would be normalized to NaN.

Signed-off-by: Walter Werner SCHNEIDER <contact@schnwalter.eu>
2025-07-28 21:28:22 +02:00
Walter Werner SCHNEIDER 36a2fa37c7 gpu: replace f32.Point/Rectangle with image.Point/Rectangle
Signed-off-by: Walter Werner SCHNEIDER <contact@schnwalter.eu>
Signed-off-by: Elias Naur <mail@eliasnaur.com>
2025-07-28 19:49:56 +02:00
Walter Werner SCHNEIDER bbb6d05f09 gpu: respect the offset fraction when clipping
Fixes: https://todo.sr.ht/~eliasnaur/gio/534
Signed-off-by: Walter Werner SCHNEIDER <contact@schnwalter.eu>
Signed-off-by: Elias Naur <mail@eliasnaur.com>
2025-07-28 19:49:56 +02:00
Walter Werner SCHNEIDER a274f6fe0f app: properly initialize editorState for tests
Signed-off-by: Walter Werner SCHNEIDER <contact@schnwalter.eu>
Signed-off-by: Elias Naur <mail@eliasnaur.com>
2025-07-16 20:39:19 +02:00
Walter Werner SCHNEIDER 0c145b3815 op/clip: add bounds expansion after move
After moving the pen, the next action should update the bounds for the start position.

References: https://todo.sr.ht/~eliasnaur/gio/451
Signed-off-by: Walter Werner SCHNEIDER <contact@schnwalter.eu>
2025-07-16 09:46:32 +02:00
Walter Werner SCHNEIDER ba82ae46d0 all: avoid collides with builtin min/max functions
Signed-off-by: Walter Werner SCHNEIDER <contact@schnwalter.eu>
2025-07-14 10:14:12 +02:00
Walter Werner SCHNEIDER 4e5a344cc2 f32: replace Affine2D{} with AffineId() for identity transformations
References: https://todo.sr.ht/~eliasnaur/gio/655
Signed-off-by: Walter Werner SCHNEIDER <contact@schnwalter.eu>
Signed-off-by: Elias Naur <mail@eliasnaur.com>
2025-07-12 15:28:03 +02:00
Walter Werner SCHNEIDER 78b54615cc app: [Wayland] add forth and fifth buttons
Signed-off-by: Walter Werner SCHNEIDER <contact@schnwalter.eu>
2025-07-12 09:43:16 +02:00
Walter Werner SCHNEIDER 31564b98c9 f32: add tests for Affine2D transformations
References: https://todo.sr.ht/~eliasnaur/gio/655
Signed-off-by: Walter Werner SCHNEIDER <contact@schnwalter.eu>
2025-07-12 09:43:08 +02:00
Walter Werner SCHNEIDER 3b1effb7f5 f32: use value receiver for Split method on Affine2D
Signed-off-by: Walter Werner SCHNEIDER <contact@schnwalter.eu>
2025-07-09 13:36:30 +02:00
Walter Werner SCHNEIDER d76b4272aa f32: replace Affine2D{} with AffineId() for identity transformations
Reduces ambiguity by introducing AffineId() for representing identity transformation matrices.

References: https://todo.sr.ht/~eliasnaur/gio/655
Signed-off-by: Walter Werner SCHNEIDER <contact@schnwalter.eu>
2025-07-09 13:35:03 +02:00
Walter Werner SCHNEIDER 3e601e73c4 app: optimize window context locking
Avoids unnecessary eglMakeCurrent and thread locking calls to enhance performance.

Fixes: https://todo.sr.ht/~eliasnaur/gio/658
Signed-off-by: Walter Werner SCHNEIDER <contact@schnwalter.eu>
2025-07-08 20:10:34 +02:00
Walter Werner SCHNEIDER b2b12d6288 widget: remove focus on click behavior from Clickable
This is a breaking change, users that need the old behavior can implement it using the existing API.

Fixes: https://todo.sr.ht/~eliasnaur/gio/511
Signed-off-by: Walter Werner SCHNEIDER <contact@schnwalter.eu>
2025-07-08 14:18:57 +02:00
Elias Naur b2f6707ad1 io/pointer: delete documentation that Cancel events have pointer IDs
Cancel events affect the entire gesture and as such all active pointers.

References: https://todo.sr.ht/~eliasnaur/gio/657
Signed-off-by: Elias Naur <mail@eliasnaur.com>
2025-07-06 09:26:55 +02:00
Walter Werner SCHNEIDER 420f4c32f4 app: [Wayland] don't recreate EGL surface during resize
Fixes: https://todo.sr.ht/~eliasnaur/gio/656
Signed-off-by: Walter Werner SCHNEIDER <contact@schnwalter.eu>
2025-07-01 15:56:50 +02:00
Elias Naur ea979b436d app: [Windows] show the maximize button even when MaxSize is set
According to @kkeybbs, pressing the maximize button on Windows only
resizes the window up to its maximum bounds. That means we can leave the
button available, and only hide it when the window has a fixed size.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2025-07-01 09:22:38 +02:00
Elias Naur d50ef687b8 app: [macOS] limit full screen window size when MaxSize is set
There are two max window size settings on macOS, `contentMaxSize` and
`maxFullScreenContentSize`. Set the latter to avoid a window being
resized larger than its maximum in full screen mode.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2025-07-01 09:16:48 +02:00
Walter Werner SCHNEIDER 6ce7ffa4ca internal/stroke: fix normal vector size and direction
The normal vector size and direction depend on the input point and the sign of the unit value provided.

Fixes: https://todo.sr.ht/~eliasnaur/gio/576
Signed-off-by: Walter Werner SCHNEIDER <contact@schnwalter.eu>
2025-06-26 09:04:57 +02:00
Walter Werner SCHNEIDER c3ce484b5e layout: add Values map to Context
Fixes: https://todo.sr.ht/~eliasnaur/gio/654
Signed-off-by: Walter Werner SCHNEIDER <contact@schnwalter.eu>
Signed-off-by: Elias Naur <mail@eliasnaur.com>
2025-06-24 13:45:59 +02:00
Miles Alan 8104d527c7 internal/debug: go 1.23.8 compat; use strings.Split not strings.SplitSeq
Currently build fails as go.mod uses go 1.23.8 which doesn't have
strings.SplitSeq.  Note: strings.SplitSeq was introduced in go 1.24.

Signed-off-by: Miles Alan <m@milesalan.com>
2025-05-30 21:13:47 -04:00
Marc 809a6d0dc7 app: [Windows] add minimal support for devices with touch screens
The Windows Pointer API
(https://learn.microsoft.com/en-us/windows/win32/api/_inputmsg/) was used
to allow the detection of events when interacting with touch screens. This
also opens the gates for supporting other types of input devices (e.g. pens
and touchpads). Mouse events are now part of the pointer events (primary
events trigger WM_POINTER<DOWN|UP>, whereas secondary ones vanilla
WM_POINTERUPDATE, and cancellations WM_POINTERCAPTURECHANGED).
A fourth and fifth button (usually found in modern mice) has also been added
for completeness, though their integration in other OS-es shall be the
objective of future patches.

Signed-off-by: Marc <marc.leroy@samantree.com>
Signed-off-by: Elias Naur <mail@eliasnaur.com>
2025-05-27 19:16:47 +02:00
Marc 0eac4f2c6a io/pointer: add forth and fifth buttons
Signed-off-by: Marc <marc.leroy@samantree.com>
Signed-off-by: Elias Naur <mail@eliasnaur.com>
2025-05-27 19:16:18 +02:00
5684185+vsariola@users.noreply.github.com 1a17e9ea37 layout: avoid heap escapes in Stack and Flex
Stack.Layout and Flex.Layout caused a lot of heap allocations / escapes. The
reason was that scratch space for dims and call and was inside Stack/FlexChild.
child.call.Add(gtx.Ops) confused the go escape analysis and caused the entired
children slice to escape to the heap, including all widgets in it. This caused a
lot of heap allocations. Now the scratch space is separate from children, and
for cases len(children) <= 32, we will allocate the scratch space on the stack.
For cases len(children) > 32, only the scratch space gets allocated from the
heap, during append.

Signed-off-by: vsariola <5684185+vsariola@users.noreply.github.com>
2025-05-26 21:10:49 +03:00
Elias Naur 0a209f7d39 flake.*: upgrade to nixpkgs 25.05, use nixpkgs android SDK
Signed-off-by: Elias Naur <mail@eliasnaur.com>
2025-05-24 11:47:43 +02:00
inkeliz 0225334124 layout,gesture: add option to handle vertical scroll on horizontal list
Previously, it was impossible to scroll a Axis == Horizontal using
ordinary mouse-wheel. Now, it's possible to set ScrollAnyAxis == True,
which combine scrolling from any direction. That makes possible
to scroll Horizontal lists with vertical mouse-wheel.

By default this option is false, keeping the old behaviour.

Signed-off-by: inkeliz <inkeliz@inkeliz.com>
2025-05-16 16:52:45 +02:00
Admin f73287be87 all: clean up code, upgrade to modern Go
Signed-off-by: ddkwork
2025-05-05 19:46:39 +02:00
Admin 86668e8b45 go.*: bump Go to 1.23, upgrade dependencies
Signed-off-by: ddkwork
2025-05-05 19:46:39 +02:00
Elias Naur e18db64991 io/pointer: remove Foremost priority
The only known use-case (nested scrolling) now works without special
treatment of the foremost handler.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2025-04-24 20:31:33 +02:00
Elias Naur efd31ad621 io/input/pointer: ignore grab commands for tags that don't have the pointer
Without this fix, two gestures that both issue GrabCmd on the same frame
will cancel each other. With the fix, the first will win the grab, and
the other ignored.

References: https://todo.sr.ht/~eliasnaur/gio/647
Signed-off-by: Elias Naur <mail@eliasnaur.com>
2025-04-24 20:05:11 +02:00
Marcel Juffermans 35ec76e516 app: [Windows] correctly center window on startup
When the window is created position it *before* processing the actions
to perform (which may include system.ActionCenter). Note that the
actions are performed in the callback windowProc().

Fixes: https://todo.sr.ht/~eliasnaur/gio/646
Signed-off-by: Marcel Juffermans <mjuffermans@paradigmone.com.au>
2025-04-24 17:06:56 +02:00
Dave Akers cc6048bc25 app: [Wayland] remove window.seat in favor of wlSeat.pointerFocus
Signed-off-by: Dave Akers <dave@dazoe.net>
2025-04-09 10:09:10 +02:00
Dave Akers 016714a668 app: [Wayland] use correct serial for wl_pointer_set_cursor, take 2
To fix #644 the serial passed to wl_pointer_set_cursor must come from
the pointer enter event. To do this we need a place to keep the serial
so I've added a field to the wlSeat struct to hold on to it.

Ref: https://wayland.app/protocols/wayland#wl_pointer:request:set_cursor
Ref: https://wayland-book.com/seat/pointer.html

Fixes: https://todo.sr.ht/~eliasnaur/gio/644
Signed-off-by: Dave Akers <dave@dazoe.net>
2025-04-03 23:59:44 +02:00
Dave Akers a3117d3823 app: [Wayland] use correct serial for wl_pointer_set_cursor
Signed-off-by: Elias Naur <mail@eliasnaur.com>
2025-04-02 21:27:53 +02:00
Elias Naur fff2375470 layout,io/input: move disabling events from layout.Context to input.Source
The fix for #605 moved the disabling of event delivery from Source
to Context to enable disabled Contexts to still react to commands
(invalidate, focus etc.). However, that change in turn caused #641 where
the exported Context.Source field would no longer know not to deliver
events.

This change partially reverts the fix for #605 by moving disabledness
back to Source, fixing #641. Disabled Sources are left capable of
executing commands, thus keeping #605 fixed.

Thanks to Chris et al for keeping the use-cases straight enough for me
to come up with this (hopefully final) fix.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
Fixes: https://todo.sr.ht/~eliasnaur/gio/641
References: https://todo.sr.ht/~eliasnaur/gio/605
2025-03-27 11:19:25 +01:00
Elias Naur 72a72a2bc2 app: [macOS] don't discard IME session for consistent snippets
An IME session must be discarded when its text content no longer matches
the underlying text component content. However, the check for matching
was too pessimistic; the IME session would be discarded if the new
snippet from the text component was not equal to the snippet reported to
the IME. This change implements a refined check that only discards a
session if the content of the overlap between the new and old snippets
don't match.

Fixes an IME issue reported by Zhang Zj.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2025-02-23 19:16:02 +01:00
Chris Waldon 14a9fbcc0d go.*: update typesetting for truncator ordering fix
This commit updates our typesetting dependency to a version that properly
bidi-orders truncator runs. This fixes an issue in which the truncator
symbol could appear on the wrong side of text.

Fixes: https://todo.sr.ht/~eliasnaur/gio/634
Signed-off-by: Chris Waldon <christopher.waldon.dev@gmail.com>
2025-02-21 07:00:52 -05:00
Elias Naur 0d23240556 gpu/internal/rendertest: allow nil pixel check functions
Signed-off-by: Elias Naur <mail@eliasnaur.com>
2025-02-18 15:18:20 +00:00
Elias Naur 77709d1771 gpu: don't store transformed rectangle paths under the same cache key
Fixes incorrect rendering of multiple transformed instances of a
rectangle.

Fixes: https://todo.sr.ht/~eliasnaur/gio/635
Signed-off-by: Elias Naur <mail@eliasnaur.com>
2025-02-18 15:14:35 +00:00
kurth4cker 4f720af6f2 app: fix miss included doc comment
License identifier is shown in documentation at[1]. This patch fixes it.

1: https://pkg.go.dev/gioui.org/app#hdr-Permissions

Signed-off-by: kurth4cker <kurth4cker@gmail.com>
2025-02-15 08:49:34 +00:00
Elias Naur af446e8b87 app: [macOS] correct error handling for newMtlContext
Fixes: https://todo.sr.ht/~eliasnaur/gio/632
Signed-off-by: Elias Naur <mail@eliasnaur.com>
2025-02-10 14:00:12 +01:00
vasijob225 95354d80bd app: [Windows] hide the maximize button when MaxSize is set
Also, disable window frame resizing when MaxSize equals MinSize.

Fixes: https://todo.sr.ht/~eliasnaur/gio/631
Signed-off-by: vasijob225 <vasijob225@protonmail.com>
2025-02-02 14:19:44 +01:00
Elias Naur a5068a1996 all: replace golang.org/x/exp/slices with the standard library package
Signed-off-by: Elias Naur <mail@eliasnaur.com>
2025-01-30 13:56:29 +01:00
Chris Waldon 593c5fbf4a text: round y offset of trailing newline
This commit tries to ensure that trailing newlines do not introduce more vertical space below
the text than is occupied by a typical text run within the text.

Signed-off-by: Chris Waldon <christopher.waldon.dev@gmail.com>
2025-01-28 10:35:52 -05:00
zjzhang adaace864d text: respect line height when layout the last empty line.
Text Shaper set the last empty line height to ascent+decent
of the paragraph break glyph which causes the last visual empty
line to have a smaller line height. This commit tries to fix it
by setting the line height using the line height from the last line.

References: https://todo.sr.ht/~eliasnaur/gio/629
Signed-off-by: zjzhang <zhangzj33@gmail.com>
2025-01-28 10:17:48 -05:00
96 changed files with 1598 additions and 814 deletions
+1 -1
View File
@@ -29,7 +29,7 @@ environment:
tasks: tasks:
- install_go: | - install_go: |
mkdir -p /home/build/sdk mkdir -p /home/build/sdk
curl -s https://dl.google.com/go/go1.22.2.linux-amd64.tar.gz | tar -C /home/build/sdk -xzf - curl -s https://dl.google.com/go/go1.24.2.linux-amd64.tar.gz | tar -C /home/build/sdk -xzf -
- prepare_toolchain: | - prepare_toolchain: |
mkdir -p $APPLE_TOOLCHAIN_ROOT mkdir -p $APPLE_TOOLCHAIN_ROOT
cd $APPLE_TOOLCHAIN_ROOT cd $APPLE_TOOLCHAIN_ROOT
+1 -1
View File
@@ -16,7 +16,7 @@ environment:
tasks: tasks:
- install_go: | - install_go: |
mkdir -p /home/build/sdk mkdir -p /home/build/sdk
curl https://dl.google.com/go/go1.22.2.freebsd-amd64.tar.gz | tar -C /home/build/sdk -xzf - curl https://dl.google.com/go/go1.24.2.freebsd-amd64.tar.gz | tar -C /home/build/sdk -xzf -
- test_gio: | - test_gio: |
cd gio cd gio
go test ./... go test ./...
+1 -1
View File
@@ -40,7 +40,7 @@ secrets:
tasks: tasks:
- install_go: | - install_go: |
mkdir -p /home/build/sdk mkdir -p /home/build/sdk
curl -s https://dl.google.com/go/go1.22.2.linux-amd64.tar.gz | tar -C /home/build/sdk -xzf - curl -s https://dl.google.com/go/go1.24.2.linux-amd64.tar.gz | tar -C /home/build/sdk -xzf -
- check_gofmt: | - check_gofmt: |
cd gio cd gio
test -z "$(gofmt -s -l .)" test -z "$(gofmt -s -l .)"
+1 -1
View File
@@ -10,7 +10,7 @@ environment:
tasks: tasks:
- install_go: | - install_go: |
mkdir -p /home/build/sdk mkdir -p /home/build/sdk
curl https://dl.google.com/go/go1.22.2.src.tar.gz | tar -C /home/build/sdk -xzf - curl https://dl.google.com/go/go1.24.2.src.tar.gz | tar -C /home/build/sdk -xzf -
cd /home/build/sdk/go/src cd /home/build/sdk/go/src
./make.bash ./make.bash
- test_gio: | - test_gio: |
+19 -22
View File
@@ -38,7 +38,24 @@ func init() {
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &wlContext{Context: ctx, win: w}, nil
surf, width, height := w.surface()
if surf == nil {
return nil, errors.New("wayland: no surface")
}
eglWin := C.wl_egl_window_create(surf, C.int(width), C.int(height))
if eglWin == nil {
return nil, errors.New("wayland: wl_egl_window_create failed")
}
eglSurf := egl.NativeWindowType(uintptr(unsafe.Pointer(eglWin)))
if err := ctx.CreateSurface(eglSurf); err != nil {
return nil, err
}
// We're in charge of the frame callbacks, don't let eglSwapBuffers
// wait for callbacks that may never arrive.
ctx.EnableVSync(false)
return &wlContext{Context: ctx, win: w, eglWin: eglWin}, nil
} }
} }
@@ -54,31 +71,11 @@ func (c *wlContext) Release() {
} }
func (c *wlContext) Refresh() error { func (c *wlContext) Refresh() error {
c.Context.ReleaseSurface()
if c.eglWin != nil {
C.wl_egl_window_destroy(c.eglWin)
c.eglWin = nil
}
surf, width, height := c.win.surface() surf, width, height := c.win.surface()
if surf == nil { if surf == nil {
return errors.New("wayland: no surface") return errors.New("wayland: no surface")
} }
eglWin := C.wl_egl_window_create(surf, C.int(width), C.int(height)) C.wl_egl_window_resize(c.eglWin, C.int(width), C.int(height), 0, 0)
if eglWin == nil {
return errors.New("wayland: wl_egl_window_create failed")
}
c.eglWin = eglWin
eglSurf := egl.NativeWindowType(uintptr(unsafe.Pointer(eglWin)))
if err := c.Context.CreateSurface(eglSurf); err != nil {
return err
}
if err := c.Context.MakeCurrent(); err != nil {
return err
}
defer c.Context.ReleaseCurrent()
// We're in charge of the frame callbacks, don't let eglSwapBuffers
// wait for callbacks that may never arrive.
c.Context.EnableVSync(false)
return nil return nil
} }
-1
View File
@@ -8,7 +8,6 @@ package app
import ( import (
"errors" "errors"
"runtime" "runtime"
"unsafe" "unsafe"
"gioui.org/gpu" "gioui.org/gpu"
+27
View File
@@ -1,9 +1,11 @@
// SPDX-License-Identifier: Unlicense OR MIT // SPDX-License-Identifier: Unlicense OR MIT
package app package app
import ( import (
"unicode" "unicode"
"unicode/utf16" "unicode/utf16"
"unicode/utf8"
"gioui.org/io/input" "gioui.org/io/input"
"gioui.org/io/key" "gioui.org/io/key"
@@ -116,3 +118,28 @@ func (e *editorState) RunesIndex(chars int) int {
// Assume runes after snippets are one UTF-16 character each. // Assume runes after snippets are one UTF-16 character each.
return runes + chars return runes + chars
} }
// areSnippetsConsistent reports whether the content of the old snippet is
// consistent with the content of the new.
func areSnippetsConsistent(old, new key.Snippet) bool {
// Compute the overlapping range.
r := old.Range
r.Start = max(r.Start, new.Start)
r.End = max(r.End, r.Start)
r.End = min(r.End, new.End)
return snippetSubstring(old, r) == snippetSubstring(new, r)
}
func snippetSubstring(s key.Snippet, r key.Range) string {
for r.Start > s.Start && r.Start < s.End {
_, n := utf8.DecodeRuneInString(s.Text)
s.Text = s.Text[n:]
s.Start++
}
for r.End < s.End && r.End > s.Start {
_, n := utf8.DecodeLastRuneInString(s.Text)
s.Text = s.Text[:len(s.Text)-n]
s.End--
}
return s.Text
}
+3
View File
@@ -6,6 +6,7 @@
package app package app
import ( import (
"gioui.org/f32"
"testing" "testing"
"unicode/utf8" "unicode/utf8"
@@ -40,6 +41,7 @@ func FuzzIME(f *testing.F) {
r.Frame(gtx.Ops) r.Frame(gtx.Ops)
var state editorState var state editorState
state.Selection.Transform = f32.AffineId()
const ( const (
cmdReplace = iota cmdReplace = iota
cmdSelect cmdSelect
@@ -139,6 +141,7 @@ func FuzzIME(f *testing.F) {
func TestEditorIndices(t *testing.T) { func TestEditorIndices(t *testing.T) {
var s editorState var s editorState
s.Selection.Transform = f32.AffineId()
const str = "Hello, 😀" const str = "Hello, 😀"
s.Snippet = key.Snippet{ s.Snippet = key.Snippet{
Text: str, Text: str,
+141 -39
View File
@@ -108,6 +108,74 @@ type MonitorInfo struct {
Flags uint32 Flags uint32
} }
type POINTER_INPUT_TYPE int32
const (
PT_POINTER POINTER_INPUT_TYPE = 1
PT_TOUCH POINTER_INPUT_TYPE = 2
PT_PEN POINTER_INPUT_TYPE = 3
PT_MOUSE POINTER_INPUT_TYPE = 4
PT_TOUCHPAD POINTER_INPUT_TYPE = 5
)
type POINTER_INFO_POINTER_FLAGS int32
const (
POINTER_FLAG_NEW POINTER_INFO_POINTER_FLAGS = 0x00000001
POINTER_FLAG_INRANGE POINTER_INFO_POINTER_FLAGS = 0x00000002
POINTER_FLAG_INCONTACT POINTER_INFO_POINTER_FLAGS = 0x00000004
POINTER_FLAG_FIRSTBUTTON POINTER_INFO_POINTER_FLAGS = 0x00000010
POINTER_FLAG_SECONDBUTTON POINTER_INFO_POINTER_FLAGS = 0x00000020
POINTER_FLAG_THIRDBUTTON POINTER_INFO_POINTER_FLAGS = 0x00000040
POINTER_FLAG_FOURTHBUTTON POINTER_INFO_POINTER_FLAGS = 0x00000080
POINTER_FLAG_FIFTHBUTTON POINTER_INFO_POINTER_FLAGS = 0x00000100
POINTER_FLAG_PRIMARY POINTER_INFO_POINTER_FLAGS = 0x00002000
POINTER_FLAG_CONFIDENCE POINTER_INFO_POINTER_FLAGS = 0x00004000
POINTER_FLAG_CANCELED POINTER_INFO_POINTER_FLAGS = 0x00008000
POINTER_FLAG_DOWN POINTER_INFO_POINTER_FLAGS = 0x00010000
POINTER_FLAG_UPDATE POINTER_INFO_POINTER_FLAGS = 0x00020000
POINTER_FLAG_UP POINTER_INFO_POINTER_FLAGS = 0x00040000
POINTER_FLAG_WHEEL POINTER_INFO_POINTER_FLAGS = 0x00080000
POINTER_FLAG_HWHEEL POINTER_INFO_POINTER_FLAGS = 0x00100000
POINTER_FLAG_CAPTURECHANGED POINTER_INFO_POINTER_FLAGS = 0x00200000
POINTER_FLAG_HASTRANSFORM POINTER_INFO_POINTER_FLAGS = 0x00400000
)
type POINTER_BUTTON_CHANGE_TYPE int32
const (
POINTER_CHANGE_NONE POINTER_BUTTON_CHANGE_TYPE = 0
POINTER_CHANGE_FIRSTBUTTON_DOWN POINTER_BUTTON_CHANGE_TYPE = 1
POINTER_CHANGE_FIRSTBUTTON_UP POINTER_BUTTON_CHANGE_TYPE = 2
POINTER_CHANGE_SECONDBUTTON_DOWN POINTER_BUTTON_CHANGE_TYPE = 3
POINTER_CHANGE_SECONDBUTTON_UP POINTER_BUTTON_CHANGE_TYPE = 4
POINTER_CHANGE_THIRDBUTTON_DOWN POINTER_BUTTON_CHANGE_TYPE = 5
POINTER_CHANGE_THIRDBUTTON_UP POINTER_BUTTON_CHANGE_TYPE = 6
POINTER_CHANGE_FOURTHBUTTON_DOWN POINTER_BUTTON_CHANGE_TYPE = 7
POINTER_CHANGE_FOURTHBUTTON_UP POINTER_BUTTON_CHANGE_TYPE = 8
POINTER_CHANGE_FIFTHBUTTON_DOWN POINTER_BUTTON_CHANGE_TYPE = 9
POINTER_CHANGE_FIFTHBUTTON_UP POINTER_BUTTON_CHANGE_TYPE = 10
)
type PointerInfo struct {
PointerType POINTER_INPUT_TYPE
PointerId uint32
FrameId uint32
PointerFlags POINTER_INFO_POINTER_FLAGS
SourceDevice syscall.Handle
HwndTarget syscall.Handle
PtPixelLocation Point
PtHimetricLocation Point
PtPixelLocationRaw Point
PtHimetricLocationRaw Point
DwTime uint32
HistoryCount uint32
InputData int32
DwKeyStates uint32
PerformanceCount uint64
ButtonChangeType POINTER_BUTTON_CHANGE_TYPE
}
const ( const (
TRUE = 1 TRUE = 1
@@ -244,45 +312,51 @@ const (
UNICODE_NOCHAR = 65535 UNICODE_NOCHAR = 65535
WM_CANCELMODE = 0x001F WM_CANCELMODE = 0x001F
WM_CHAR = 0x0102 WM_CHAR = 0x0102
WM_CLOSE = 0x0010 WM_CLOSE = 0x0010
WM_CREATE = 0x0001 WM_CREATE = 0x0001
WM_DPICHANGED = 0x02E0 WM_DPICHANGED = 0x02E0
WM_DESTROY = 0x0002 WM_DESTROY = 0x0002
WM_ERASEBKGND = 0x0014 WM_ERASEBKGND = 0x0014
WM_GETMINMAXINFO = 0x0024 WM_GETMINMAXINFO = 0x0024
WM_IME_COMPOSITION = 0x010F WM_IME_COMPOSITION = 0x010F
WM_IME_ENDCOMPOSITION = 0x010E WM_IME_ENDCOMPOSITION = 0x010E
WM_IME_STARTCOMPOSITION = 0x010D WM_IME_STARTCOMPOSITION = 0x010D
WM_KEYDOWN = 0x0100 WM_KEYDOWN = 0x0100
WM_KEYUP = 0x0101 WM_KEYUP = 0x0101
WM_KILLFOCUS = 0x0008 WM_KILLFOCUS = 0x0008
WM_LBUTTONDOWN = 0x0201 WM_LBUTTONDOWN = 0x0201
WM_LBUTTONUP = 0x0202 WM_LBUTTONUP = 0x0202
WM_MBUTTONDOWN = 0x0207 WM_MBUTTONDOWN = 0x0207
WM_MBUTTONUP = 0x0208 WM_MBUTTONUP = 0x0208
WM_MOUSEMOVE = 0x0200 WM_MOUSEMOVE = 0x0200
WM_MOUSEWHEEL = 0x020A WM_MOUSEWHEEL = 0x020A
WM_MOUSEHWHEEL = 0x020E WM_MOUSEHWHEEL = 0x020E
WM_NCACTIVATE = 0x0086 WM_NCACTIVATE = 0x0086
WM_NCHITTEST = 0x0084 WM_NCHITTEST = 0x0084
WM_NCCALCSIZE = 0x0083 WM_NCCALCSIZE = 0x0083
WM_PAINT = 0x000F WM_PAINT = 0x000F
WM_QUIT = 0x0012 WM_POINTERCAPTURECHANGED = 0x024C
WM_SETCURSOR = 0x0020 WM_POINTERDOWN = 0x0246
WM_SETFOCUS = 0x0007 WM_POINTERUP = 0x0247
WM_SHOWWINDOW = 0x0018 WM_POINTERUPDATE = 0x0245
WM_SIZE = 0x0005 WM_POINTERWHEEL = 0x024E
WM_STYLECHANGED = 0x007D WM_POINTERHWHEEL = 0x024F
WM_SYSKEYDOWN = 0x0104 WM_QUIT = 0x0012
WM_SYSKEYUP = 0x0105 WM_RBUTTONDOWN = 0x0204
WM_RBUTTONDOWN = 0x0204 WM_RBUTTONUP = 0x0205
WM_RBUTTONUP = 0x0205 WM_SETCURSOR = 0x0020
WM_TIMER = 0x0113 WM_SETFOCUS = 0x0007
WM_UNICHAR = 0x0109 WM_SHOWWINDOW = 0x0018
WM_USER = 0x0400 WM_SIZE = 0x0005
WM_WINDOWPOSCHANGED = 0x0047 WM_STYLECHANGED = 0x007D
WM_SYSKEYDOWN = 0x0104
WM_SYSKEYUP = 0x0105
WM_TIMER = 0x0113
WM_UNICHAR = 0x0109
WM_USER = 0x0400
WM_WINDOWPOSCHANGED = 0x0047
WS_CLIPCHILDREN = 0x02000000 WS_CLIPCHILDREN = 0x02000000
WS_CLIPSIBLINGS = 0x04000000 WS_CLIPSIBLINGS = 0x04000000
@@ -346,6 +420,7 @@ var (
_DestroyWindow = user32.NewProc("DestroyWindow") _DestroyWindow = user32.NewProc("DestroyWindow")
_DispatchMessage = user32.NewProc("DispatchMessageW") _DispatchMessage = user32.NewProc("DispatchMessageW")
_EmptyClipboard = user32.NewProc("EmptyClipboard") _EmptyClipboard = user32.NewProc("EmptyClipboard")
_EnableMouseInPointer = user32.NewProc("EnableMouseInPointer")
_GetWindowRect = user32.NewProc("GetWindowRect") _GetWindowRect = user32.NewProc("GetWindowRect")
_GetClientRect = user32.NewProc("GetClientRect") _GetClientRect = user32.NewProc("GetClientRect")
_GetClipboardData = user32.NewProc("GetClipboardData") _GetClipboardData = user32.NewProc("GetClipboardData")
@@ -355,6 +430,7 @@ var (
_GetMessage = user32.NewProc("GetMessageW") _GetMessage = user32.NewProc("GetMessageW")
_GetMessageTime = user32.NewProc("GetMessageTime") _GetMessageTime = user32.NewProc("GetMessageTime")
_GetMonitorInfo = user32.NewProc("GetMonitorInfoW") _GetMonitorInfo = user32.NewProc("GetMonitorInfoW")
_GetPointerInfo = user32.NewProc("GetPointerInfo")
_GetSystemMetrics = user32.NewProc("GetSystemMetrics") _GetSystemMetrics = user32.NewProc("GetSystemMetrics")
_GetWindowLong = user32.NewProc("GetWindowLongPtrW") _GetWindowLong = user32.NewProc("GetWindowLongPtrW")
_GetWindowLong32 = user32.NewProc("GetWindowLongW") _GetWindowLong32 = user32.NewProc("GetWindowLongW")
@@ -372,6 +448,7 @@ var (
_PostQuitMessage = user32.NewProc("PostQuitMessage") _PostQuitMessage = user32.NewProc("PostQuitMessage")
_ReleaseCapture = user32.NewProc("ReleaseCapture") _ReleaseCapture = user32.NewProc("ReleaseCapture")
_RegisterClassExW = user32.NewProc("RegisterClassExW") _RegisterClassExW = user32.NewProc("RegisterClassExW")
_RegisterTouchWindow = user32.NewProc("RegisterTouchWindow")
_ReleaseDC = user32.NewProc("ReleaseDC") _ReleaseDC = user32.NewProc("ReleaseDC")
_ScreenToClient = user32.NewProc("ScreenToClient") _ScreenToClient = user32.NewProc("ScreenToClient")
_ShowWindow = user32.NewProc("ShowWindow") _ShowWindow = user32.NewProc("ShowWindow")
@@ -445,6 +522,31 @@ func CreateWindowEx(dwExStyle uint32, lpClassName uint16, lpWindowName string, d
return syscall.Handle(hwnd), nil return syscall.Handle(hwnd), nil
} }
func GetPointerInfo(pointerId uint32) (PointerInfo, error) {
var info PointerInfo
r1, _, err := _GetPointerInfo.Call(uintptr(pointerId), uintptr(unsafe.Pointer(&info)))
if r1 == 0 {
return PointerInfo{}, fmt.Errorf("GetPointerInfo failed: %v", err)
}
return info, nil
}
func RegisterTouchWindow(hwnd syscall.Handle, flags uint32) error {
r1, _, err := _RegisterTouchWindow.Call(uintptr(hwnd), uintptr(flags))
if r1 == 0 {
return fmt.Errorf("RegisterTouchWindow failed: %v", err)
}
return nil
}
func EnableMouseInPointer(enable uint) error {
r1, _, err := _EnableMouseInPointer.Call(uintptr(enable))
if r1 == 0 {
return fmt.Errorf("EnableMouseInPointer failed: %v", err)
}
return nil
}
func DefWindowProc(hwnd syscall.Handle, msg uint32, wparam, lparam uintptr) uintptr { func DefWindowProc(hwnd syscall.Handle, msg uint32, wparam, lparam uintptr) uintptr {
r, _, _ := _DefWindowProc.Call(uintptr(hwnd), uintptr(msg), wparam, lparam) r, _, _ := _DefWindowProc.Call(uintptr(hwnd), uintptr(msg), wparam, lparam)
return r return r
+1 -1
View File
@@ -96,7 +96,7 @@ func newMtlContext(w *window) (*mtlContext, error) {
return nil, errors.New("metal: CAMetalLayer construction failed") return nil, errors.New("metal: CAMetalLayer construction failed")
} }
queue := C.newCommandQueue(dev) queue := C.newCommandQueue(dev)
if layer == 0 { if queue == 0 {
C.CFRelease(dev) C.CFRelease(dev)
C.CFRelease(layer) C.CFRelease(layer)
return nil, errors.New("metal: [MTLDevice newCommandQueue] failed") return nil, errors.New("metal: [MTLDevice newCommandQueue] failed")
+10 -17
View File
@@ -5,7 +5,7 @@ package app
/* /*
#include <Foundation/Foundation.h> #include <Foundation/Foundation.h>
__attribute__ ((visibility ("hidden"))) void gio_wakeupMainThread(void); __attribute__ ((visibility ("hidden"))) void gio_runOnMain(uintptr_t h);
__attribute__ ((visibility ("hidden"))) CFTypeRef gio_createDisplayLink(void); __attribute__ ((visibility ("hidden"))) CFTypeRef gio_createDisplayLink(void);
__attribute__ ((visibility ("hidden"))) void gio_releaseDisplayLink(CFTypeRef dl); __attribute__ ((visibility ("hidden"))) void gio_releaseDisplayLink(CFTypeRef dl);
__attribute__ ((visibility ("hidden"))) int gio_startDisplayLink(CFTypeRef dl); __attribute__ ((visibility ("hidden"))) int gio_startDisplayLink(CFTypeRef dl);
@@ -40,8 +40,10 @@ static CFTypeRef newNSString(unichar *chars, NSUInteger length) {
} }
*/ */
import "C" import "C"
import ( import (
"errors" "errors"
"runtime/cgo"
"sync" "sync"
"sync/atomic" "sync/atomic"
"time" "time"
@@ -73,8 +75,6 @@ type displayLink struct {
// displayLinks maps CFTypeRefs to *displayLinks. // displayLinks maps CFTypeRefs to *displayLinks.
var displayLinks sync.Map var displayLinks sync.Map
var mainFuncs = make(chan func(), 1)
func isMainThread() bool { func isMainThread() bool {
return bool(C.isMainThread()) return bool(C.isMainThread())
} }
@@ -85,22 +85,15 @@ func runOnMain(f func()) {
f() f()
return return
} }
go func() { C.gio_runOnMain(C.uintptr_t(cgo.NewHandle(f)))
mainFuncs <- f
C.gio_wakeupMainThread()
}()
} }
//export gio_dispatchMainFuncs //export gio_runFunc
func gio_dispatchMainFuncs() { func gio_runFunc(h C.uintptr_t) {
for { handle := cgo.Handle(h)
select { defer handle.Delete()
case f := <-mainFuncs: f := handle.Value().(func())
f() f()
default:
return
}
}
} }
// nsstringToString converts a NSString to a Go string. // nsstringToString converts a NSString to a Go string.
+2 -2
View File
@@ -4,8 +4,8 @@
#include "_cgo_export.h" #include "_cgo_export.h"
void gio_wakeupMainThread(void) { void gio_runOnMain(uintptr_t h) {
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
gio_dispatchMainFuncs(); gio_runFunc(h);
}); });
} }
+8 -4
View File
@@ -40,8 +40,9 @@ import (
#define MOUSE_SCROLL 4 #define MOUSE_SCROLL 4
__attribute__ ((visibility ("hidden"))) void gio_main(void); __attribute__ ((visibility ("hidden"))) void gio_main(void);
__attribute__ ((visibility ("hidden"))) void gio_init(void);
__attribute__ ((visibility ("hidden"))) CFTypeRef gio_createView(int presentWithTrans); __attribute__ ((visibility ("hidden"))) CFTypeRef gio_createView(int presentWithTrans);
__attribute__ ((visibility ("hidden"))) CFTypeRef gio_createWindow(CFTypeRef viewRef, CGFloat width, CGFloat height, CGFloat minWidth, CGFloat minHeight, CGFloat maxWidth, CGFloat maxHeight); __attribute__ ((visibility ("hidden"))) CFTypeRef gio_createWindow(CFTypeRef viewRef, CGFloat width, CGFloat height);
__attribute__ ((visibility ("hidden"))) void gio_viewSetHandle(CFTypeRef viewRef, uintptr_t handle); __attribute__ ((visibility ("hidden"))) void gio_viewSetHandle(CFTypeRef viewRef, uintptr_t handle);
static void writeClipboard(CFTypeRef str) { static void writeClipboard(CFTypeRef str) {
@@ -192,6 +193,7 @@ static void setMaxSize(CFTypeRef windowRef, CGFloat width, CGFloat height) {
@autoreleasepool { @autoreleasepool {
NSWindow* window = (__bridge NSWindow *)windowRef; NSWindow* window = (__bridge NSWindow *)windowRef;
window.contentMaxSize = NSMakeSize(width, height); window.contentMaxSize = NSMakeSize(width, height);
window.maxFullScreenContentSize = NSMakeSize(width, height);
} }
} }
@@ -333,6 +335,8 @@ import "C"
func init() { func init() {
// Darwin requires that UI operations happen on the main thread only. // Darwin requires that UI operations happen on the main thread only.
runtime.LockOSThread() runtime.LockOSThread()
// Register launch finished listener.
C.gio_init()
} }
// AppKitViewEvent notifies the client of changes to the window AppKit handles. // AppKitViewEvent notifies the client of changes to the window AppKit handles.
@@ -534,7 +538,7 @@ func (w *window) SetCursor(cursor pointer.Cursor) {
} }
func (w *window) EditorStateChanged(old, new editorState) { func (w *window) EditorStateChanged(old, new editorState) {
if old.Selection.Range != new.Selection.Range || old.Snippet != new.Snippet { if old.Selection.Range != new.Selection.Range || !areSnippetsConsistent(old.Snippet, new.Snippet) {
C.discardMarkedText(w.view) C.discardMarkedText(w.view)
w.w.SetComposingRegion(key.Range{Start: -1, End: -1}) w.w.SetComposingRegion(key.Range{Start: -1, End: -1})
} }
@@ -892,7 +896,7 @@ func gio_firstRectForCharacterRange(h C.uintptr_t, crng C.NSRange, actual C.NSRa
// Transform to NSView local coordinates (lower left origin, undo backing scale). // Transform to NSView local coordinates (lower left origin, undo backing scale).
scale := 1. / float32(C.getViewBackingScale(w.view)) scale := 1. / float32(C.getViewBackingScale(w.view))
height := float32(C.viewHeight(w.view)) height := float32(C.viewHeight(w.view))
local := f32.Affine2D{}.Scale(f32.Pt(0, 0), f32.Pt(scale, -scale)).Offset(f32.Pt(0, height)) local := f32.AffineId().Scale(f32.Pt(0, 0), f32.Pt(scale, -scale)).Offset(f32.Pt(0, height))
t := local.Mul(sel.Transform) t := local.Mul(sel.Transform)
bounds := f32.Rectangle{ bounds := f32.Rectangle{
Min: t.Transform(sel.Pos.Sub(f32.Pt(0, sel.Ascent))), Min: t.Transform(sel.Pos.Sub(f32.Pt(0, sel.Ascent))),
@@ -1010,7 +1014,7 @@ func newWindow(win *callbacks, options []Option) {
w.ProcessEvent(DestroyEvent{Err: err}) w.ProcessEvent(DestroyEvent{Err: err})
return return
} }
window := C.gio_createWindow(w.view, C.CGFloat(cnf.Size.X), C.CGFloat(cnf.Size.Y), 0, 0, 0, 0) window := C.gio_createWindow(w.view, C.CGFloat(cnf.Size.X), C.CGFloat(cnf.Size.Y))
// Release our reference now that the NSWindow has it. // Release our reference now that the NSWindow has it.
C.CFRelease(w.view) C.CFRelease(w.view)
w.Configure(options) w.Configure(options)
+23 -8
View File
@@ -369,7 +369,7 @@ void gio_setCursor(NSUInteger curID) {
} }
} }
CFTypeRef gio_createWindow(CFTypeRef viewRef, CGFloat width, CGFloat height, CGFloat minWidth, CGFloat minHeight, CGFloat maxWidth, CGFloat maxHeight) { CFTypeRef gio_createWindow(CFTypeRef viewRef, CGFloat width, CGFloat height) {
@autoreleasepool { @autoreleasepool {
NSRect rect = NSMakeRect(0, 0, width, height); NSRect rect = NSMakeRect(0, 0, width, height);
NSUInteger styleMask = NSTitledWindowMask | NSUInteger styleMask = NSTitledWindowMask |
@@ -381,12 +381,6 @@ CFTypeRef gio_createWindow(CFTypeRef viewRef, CGFloat width, CGFloat height, CGF
styleMask:styleMask styleMask:styleMask
backing:NSBackingStoreBuffered backing:NSBackingStoreBuffered
defer:NO]; defer:NO];
if (minWidth > 0 || minHeight > 0) {
window.contentMinSize = NSMakeSize(minWidth, minHeight);
}
if (maxWidth > 0 || maxHeight > 0) {
window.contentMaxSize = NSMakeSize(maxWidth, maxHeight);
}
[window setAcceptsMouseMovedEvents:YES]; [window setAcceptsMouseMovedEvents:YES];
NSView *view = (__bridge NSView *)viewRef; NSView *view = (__bridge NSView *)viewRef;
[window setContentView:view]; [window setContentView:view];
@@ -426,7 +420,6 @@ void gio_viewSetHandle(CFTypeRef viewRef, uintptr_t handle) {
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification { - (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
[NSApp activateIgnoringOtherApps:YES]; [NSApp activateIgnoringOtherApps:YES];
gio_onFinishLaunching();
} }
@end @end
@@ -457,3 +450,25 @@ void gio_main() {
[NSApp run]; [NSApp run];
} }
} }
@interface AppListener : NSObject
@end
static AppListener *appListener;
@implementation AppListener
- (void)launchFinished:(NSNotification *)notification {
appListener = nil;
gio_onFinishLaunching();
}
@end
void gio_init() {
@autoreleasepool {
appListener = [[AppListener alloc] init];
[[NSNotificationCenter defaultCenter] addObserver:appListener
selector:@selector(launchFinished:)
name:NSApplicationDidFinishLaunchingNotification
object:nil];
}
}
+26 -14
View File
@@ -116,6 +116,8 @@ type wlSeat struct {
// The most recent input serial. // The most recent input serial.
serial C.uint32_t serial C.uint32_t
// The most recent pointer enter serial.
pointerSerial C.uint32_t
pointerFocus *window pointerFocus *window
keyboardFocus *window keyboardFocus *window
@@ -154,7 +156,6 @@ type repeatState struct {
type window struct { type window struct {
w *callbacks w *callbacks
disp *wlDisplay disp *wlDisplay
seat *wlSeat
surf *C.struct_wl_surface surf *C.struct_wl_surface
wmSurf *C.struct_xdg_surface wmSurf *C.struct_xdg_surface
topLvl *C.struct_xdg_toplevel topLvl *C.struct_xdg_toplevel
@@ -851,8 +852,8 @@ func gio_onTouchCancel(data unsafe.Pointer, touch *C.struct_wl_touch) {
func gio_onPointerEnter(data unsafe.Pointer, pointer *C.struct_wl_pointer, serial C.uint32_t, surf *C.struct_wl_surface, x, y C.wl_fixed_t) { func gio_onPointerEnter(data unsafe.Pointer, pointer *C.struct_wl_pointer, serial C.uint32_t, surf *C.struct_wl_surface, x, y C.wl_fixed_t) {
s := callbackLoad(data).(*wlSeat) s := callbackLoad(data).(*wlSeat)
s.serial = serial s.serial = serial
s.pointerSerial = serial
w := callbackLoad(unsafe.Pointer(surf)).(*window) w := callbackLoad(unsafe.Pointer(surf)).(*window)
w.seat = s
s.pointerFocus = w s.pointerFocus = w
w.setCursor(pointer, serial) w.setCursor(pointer, serial)
w.lastPos = f32.Point{X: fromFixed(x), Y: fromFixed(y)} w.lastPos = f32.Point{X: fromFixed(x), Y: fromFixed(y)}
@@ -861,9 +862,9 @@ func gio_onPointerEnter(data unsafe.Pointer, pointer *C.struct_wl_pointer, seria
//export gio_onPointerLeave //export gio_onPointerLeave
func gio_onPointerLeave(data unsafe.Pointer, p *C.struct_wl_pointer, serial C.uint32_t, surf *C.struct_wl_surface) { func gio_onPointerLeave(data unsafe.Pointer, p *C.struct_wl_pointer, serial C.uint32_t, surf *C.struct_wl_surface) {
w := callbackLoad(unsafe.Pointer(surf)).(*window) w := callbackLoad(unsafe.Pointer(surf)).(*window)
w.seat = nil
s := callbackLoad(data).(*wlSeat) s := callbackLoad(data).(*wlSeat)
s.serial = serial s.serial = serial
s.pointerFocus = nil
if w.inCompositor { if w.inCompositor {
w.inCompositor = false w.inCompositor = false
w.ProcessEvent(pointer.Event{Kind: pointer.Cancel}) w.ProcessEvent(pointer.Event{Kind: pointer.Cancel})
@@ -883,11 +884,13 @@ func gio_onPointerButton(data unsafe.Pointer, p *C.struct_wl_pointer, serial, t,
s := callbackLoad(data).(*wlSeat) s := callbackLoad(data).(*wlSeat)
s.serial = serial s.serial = serial
w := s.pointerFocus w := s.pointerFocus
// From linux-event-codes.h. // From Linux: include/uapi/linux/input-event-codes.h
const ( const (
BTN_LEFT = 0x110 BTN_LEFT = 0x110
BTN_RIGHT = 0x111 BTN_RIGHT = 0x111
BTN_MIDDLE = 0x112 BTN_MIDDLE = 0x112
BTN_SIDE = 0x113
BTN_EXTRA = 0x114
) )
var btn pointer.Buttons var btn pointer.Buttons
switch wbtn { switch wbtn {
@@ -897,6 +900,10 @@ func gio_onPointerButton(data unsafe.Pointer, p *C.struct_wl_pointer, serial, t,
btn = pointer.ButtonSecondary btn = pointer.ButtonSecondary
case BTN_MIDDLE: case BTN_MIDDLE:
btn = pointer.ButtonTertiary btn = pointer.ButtonTertiary
case BTN_SIDE:
btn = pointer.ButtonQuaternary
case BTN_EXTRA:
btn = pointer.ButtonQuinary
default: default:
return return
} }
@@ -963,6 +970,9 @@ func gio_onPointerAxis(data unsafe.Pointer, p *C.struct_wl_pointer, t, axis C.ui
func gio_onPointerFrame(data unsafe.Pointer, p *C.struct_wl_pointer) { func gio_onPointerFrame(data unsafe.Pointer, p *C.struct_wl_pointer) {
s := callbackLoad(data).(*wlSeat) s := callbackLoad(data).(*wlSeat)
w := s.pointerFocus w := s.pointerFocus
if w == nil {
return
}
w.flushScroll() w.flushScroll()
w.flushFling() w.flushFling()
} }
@@ -1143,16 +1153,17 @@ func (w *window) Perform(actions system.Action) {
} }
func (w *window) move(serial C.uint32_t) { func (w *window) move(serial C.uint32_t) {
s := w.seat s := w.disp.seat
if !w.inCompositor && s != nil { if w.inCompositor || s.pointerFocus != w {
w.inCompositor = true return
C.xdg_toplevel_move(w.topLvl, s.seat, serial)
} }
w.inCompositor = true
C.xdg_toplevel_move(w.topLvl, s.seat, serial)
} }
func (w *window) resize(serial, edge C.uint32_t) { func (w *window) resize(serial, edge C.uint32_t) {
s := w.seat s := w.disp.seat
if w.inCompositor || s == nil { if w.inCompositor || s.pointerFocus != w {
return return
} }
w.inCompositor = true w.inCompositor = true
@@ -1165,11 +1176,12 @@ func (w *window) SetCursor(cursor pointer.Cursor) {
} }
func (w *window) updateCursor() { func (w *window) updateCursor() {
ptr := w.disp.seat.pointer s := w.disp.seat
if ptr == nil { ptr := s.pointer
if ptr == nil || s.pointerFocus != w {
return return
} }
w.setCursor(ptr, w.serial) w.setCursor(ptr, s.pointerSerial)
} }
func (w *window) setCursor(pointer *C.struct_wl_pointer, serial C.uint32_t) { func (w *window) setCursor(pointer *C.struct_wl_pointer, serial C.uint32_t) {
@@ -1178,7 +1190,7 @@ func (w *window) setCursor(pointer *C.struct_wl_pointer, serial C.uint32_t) {
c = w.cursor.cursor c = w.cursor.cursor
} }
if c == nil { if c == nil {
C.wl_pointer_set_cursor(pointer, w.serial, nil, 0, 0) C.wl_pointer_set_cursor(pointer, serial, nil, 0, 0)
return return
} }
// Get images[0]. // Get images[0].
+108 -49
View File
@@ -36,10 +36,9 @@ type Win32ViewEvent struct {
} }
type window struct { type window struct {
hwnd syscall.Handle hwnd syscall.Handle
hdc syscall.Handle hdc syscall.Handle
w *callbacks w *callbacks
pointerBtns pointer.Buttons
// cursorIn tracks whether the cursor was inside the window according // cursorIn tracks whether the cursor was inside the window according
// to the most recent WM_SETCURSOR. // to the most recent WM_SETCURSOR.
@@ -175,6 +174,12 @@ func (w *window) init() error {
if err != nil { if err != nil {
return err return err
} }
if err := windows.RegisterTouchWindow(hwnd, 0); err != nil {
return err
}
if err := windows.EnableMouseInPointer(1); err != nil {
return err
}
w.hdc, err = windows.GetDC(hwnd) w.hdc, err = windows.GetDC(hwnd)
if err != nil { if err != nil {
windows.DestroyWindow(hwnd) windows.DestroyWindow(hwnd)
@@ -265,18 +270,32 @@ func windowProc(hwnd syscall.Handle, msg uint32, wParam, lParam uintptr) uintptr
return 0 return 0
} }
} }
case windows.WM_LBUTTONDOWN: case windows.WM_POINTERDOWN, windows.WM_POINTERUP, windows.WM_POINTERUPDATE, windows.WM_POINTERCAPTURECHANGED:
w.pointerButton(pointer.ButtonPrimary, true, lParam, getModifiers()) pid := getPointerIDwParam(wParam)
case windows.WM_LBUTTONUP: pi, err := windows.GetPointerInfo(uint32(pid))
w.pointerButton(pointer.ButtonPrimary, false, lParam, getModifiers()) if err != nil {
case windows.WM_RBUTTONDOWN: panic(err)
w.pointerButton(pointer.ButtonSecondary, true, lParam, getModifiers()) }
case windows.WM_RBUTTONUP: switch msg {
w.pointerButton(pointer.ButtonSecondary, false, lParam, getModifiers()) case windows.WM_POINTERDOWN:
case windows.WM_MBUTTONDOWN: windows.SetCapture(w.hwnd)
w.pointerButton(pointer.ButtonTertiary, true, lParam, getModifiers()) case windows.WM_POINTERUP:
case windows.WM_MBUTTONUP: windows.ReleaseCapture()
w.pointerButton(pointer.ButtonTertiary, false, lParam, getModifiers()) }
kind := pointer.Move
switch pi.ButtonChangeType {
case windows.POINTER_CHANGE_FIRSTBUTTON_DOWN, windows.POINTER_CHANGE_SECONDBUTTON_DOWN, windows.POINTER_CHANGE_THIRDBUTTON_DOWN, windows.POINTER_CHANGE_FOURTHBUTTON_DOWN, windows.POINTER_CHANGE_FIFTHBUTTON_DOWN:
kind = pointer.Press
case windows.POINTER_CHANGE_FIRSTBUTTON_UP, windows.POINTER_CHANGE_SECONDBUTTON_UP, windows.POINTER_CHANGE_THIRDBUTTON_UP, windows.POINTER_CHANGE_FOURTHBUTTON_UP, windows.POINTER_CHANGE_FIFTHBUTTON_UP:
kind = pointer.Release
}
if (pi.PointerFlags&windows.POINTER_FLAG_CANCELED != 0) || (msg == windows.WM_POINTERCAPTURECHANGED) {
kind = pointer.Cancel
}
w.pointerUpdate(pi, pid, kind, lParam)
case windows.WM_CANCELMODE: case windows.WM_CANCELMODE:
w.ProcessEvent(pointer.Event{ w.ProcessEvent(pointer.Event{
Kind: pointer.Cancel, Kind: pointer.Cancel,
@@ -296,20 +315,9 @@ func windowProc(hwnd syscall.Handle, msg uint32, wParam, lParam uintptr) uintptr
np := windows.Point{X: int32(x), Y: int32(y)} np := windows.Point{X: int32(x), Y: int32(y)}
windows.ScreenToClient(w.hwnd, &np) windows.ScreenToClient(w.hwnd, &np)
return w.hitTest(int(np.X), int(np.Y)) return w.hitTest(int(np.X), int(np.Y))
case windows.WM_MOUSEMOVE: case windows.WM_POINTERWHEEL:
x, y := coordsFromlParam(lParam)
p := f32.Point{X: float32(x), Y: float32(y)}
w.ProcessEvent(pointer.Event{
Kind: pointer.Move,
Source: pointer.Mouse,
Position: p,
Buttons: w.pointerBtns,
Time: windows.GetMessageTime(),
Modifiers: getModifiers(),
})
case windows.WM_MOUSEWHEEL:
w.scrollEvent(wParam, lParam, false, getModifiers()) w.scrollEvent(wParam, lParam, false, getModifiers())
case windows.WM_MOUSEHWHEEL: case windows.WM_POINTERHWHEEL:
w.scrollEvent(wParam, lParam, true, getModifiers()) w.scrollEvent(wParam, lParam, true, getModifiers())
case windows.WM_DESTROY: case windows.WM_DESTROY:
w.ProcessEvent(Win32ViewEvent{}) w.ProcessEvent(Win32ViewEvent{})
@@ -499,34 +507,28 @@ func (w *window) hitTest(x, y int) uintptr {
return windows.HTCLIENT return windows.HTCLIENT
} }
func (w *window) pointerButton(btn pointer.Buttons, press bool, lParam uintptr, kmods key.Modifiers) { func (w *window) pointerUpdate(pi windows.PointerInfo, pid pointer.ID, kind pointer.Kind, lParam uintptr) {
if !w.config.Focused { if !w.config.Focused {
windows.SetFocus(w.hwnd) windows.SetFocus(w.hwnd)
} }
var kind pointer.Kind src := pointer.Touch
if press { if pi.PointerType == windows.PT_MOUSE {
kind = pointer.Press src = pointer.Mouse
if w.pointerBtns == 0 {
windows.SetCapture(w.hwnd)
}
w.pointerBtns |= btn
} else {
kind = pointer.Release
w.pointerBtns &^= btn
if w.pointerBtns == 0 {
windows.ReleaseCapture()
}
} }
x, y := coordsFromlParam(lParam) x, y := coordsFromlParam(lParam)
p := f32.Point{X: float32(x), Y: float32(y)} np := windows.Point{X: int32(x), Y: int32(y)}
windows.ScreenToClient(w.hwnd, &np)
p := f32.Point{X: float32(np.X), Y: float32(np.Y)}
w.ProcessEvent(pointer.Event{ w.ProcessEvent(pointer.Event{
Kind: kind, Kind: kind,
Source: pointer.Mouse, Source: src,
Position: p, Position: p,
Buttons: w.pointerBtns, PointerID: pid,
Buttons: getPointerButtons(pi),
Time: windows.GetMessageTime(), Time: windows.GetMessageTime(),
Modifiers: kmods, Modifiers: getModifiers(),
}) })
} }
@@ -537,6 +539,12 @@ func coordsFromlParam(lParam uintptr) (int, int) {
} }
func (w *window) scrollEvent(wParam, lParam uintptr, horizontal bool, kmods key.Modifiers) { func (w *window) scrollEvent(wParam, lParam uintptr, horizontal bool, kmods key.Modifiers) {
pid := getPointerIDwParam(wParam)
pi, err := windows.GetPointerInfo(uint32(pid))
if err != nil {
panic(err)
}
x, y := coordsFromlParam(lParam) x, y := coordsFromlParam(lParam)
// The WM_MOUSEWHEEL coordinates are in screen coordinates, in contrast // The WM_MOUSEWHEEL coordinates are in screen coordinates, in contrast
// to other mouse events. // to other mouse events.
@@ -559,7 +567,7 @@ func (w *window) scrollEvent(wParam, lParam uintptr, horizontal bool, kmods key.
Kind: pointer.Scroll, Kind: pointer.Scroll,
Source: pointer.Mouse, Source: pointer.Mouse,
Position: p, Position: p,
Buttons: w.pointerBtns, Buttons: getPointerButtons(pi),
Scroll: sp, Scroll: sp,
Modifiers: kmods, Modifiers: kmods,
Time: windows.GetMessageTime(), Time: windows.GetMessageTime(),
@@ -750,8 +758,17 @@ func (w *window) Configure(options []Option) {
swpStyle |= windows.SWP_NOMOVE | windows.SWP_NOSIZE swpStyle |= windows.SWP_NOMOVE | windows.SWP_NOSIZE
showMode = windows.SW_SHOWMAXIMIZED showMode = windows.SW_SHOWMAXIMIZED
} }
windows.SetWindowLong(w.hwnd, windows.GWL_STYLE, style)
// Disable window resizing if MinSize and MaxSize are equal.
if cnf.MaxSize != (image.Point{}) && cnf.MinSize == cnf.MaxSize {
style &^= windows.WS_MAXIMIZEBOX
style &^= windows.WS_THICKFRAME
}
// Note: these invocation all trigger the windows callback method which may process a pending system.ActionCenter
// action, so SetWindowPos should come first so as to not "overwrite" system.ActionCenter.
windows.SetWindowPos(w.hwnd, 0, x, y, width, height, swpStyle) windows.SetWindowPos(w.hwnd, 0, x, y, width, height, swpStyle)
windows.SetWindowLong(w.hwnd, windows.GWL_STYLE, style)
windows.ShowWindow(w.hwnd, showMode) windows.ShowWindow(w.hwnd, showMode)
} }
@@ -993,3 +1010,45 @@ func (Win32ViewEvent) ImplementsEvent() {}
func (w Win32ViewEvent) Valid() bool { func (w Win32ViewEvent) Valid() bool {
return w != (Win32ViewEvent{}) return w != (Win32ViewEvent{})
} }
// LOWORD (minwindef.h)
func loWord(val uint32) uint16 {
return uint16(val & 0xFFFF)
}
// GET_POINTERID_WPARAM (winuser.h)
func getPointerIDwParam(wParam uintptr) pointer.ID {
return pointer.ID(loWord(uint32(wParam)))
}
func getPointerButtons(pi windows.PointerInfo) pointer.Buttons {
var btns pointer.Buttons
if pi.PointerFlags&windows.POINTER_FLAG_FIRSTBUTTON != 0 {
btns |= pointer.ButtonPrimary
} else {
btns &^= pointer.ButtonPrimary
}
if pi.PointerFlags&windows.POINTER_FLAG_SECONDBUTTON != 0 {
btns |= pointer.ButtonSecondary
} else {
btns &^= pointer.ButtonSecondary
}
if pi.PointerFlags&windows.POINTER_FLAG_THIRDBUTTON != 0 {
btns |= pointer.ButtonTertiary
} else {
btns &^= pointer.ButtonTertiary
}
if pi.PointerFlags&windows.POINTER_FLAG_FOURTHBUTTON != 0 {
btns |= pointer.ButtonQuaternary
} else {
btns &^= pointer.ButtonQuaternary
}
if pi.PointerFlags&windows.POINTER_FLAG_FIFTHBUTTON != 0 {
btns |= pointer.ButtonQuinary
} else {
btns &^= pointer.ButtonQuinary
}
return btns
}
+2 -3
View File
@@ -26,6 +26,7 @@ package app
*/ */
import "C" import "C"
import ( import (
"errors" "errors"
"fmt" "fmt"
@@ -751,9 +752,7 @@ func (h *x11EventHandler) handleEvents() bool {
return redraw return redraw
} }
var ( var x11Threads sync.Once
x11Threads sync.Once
)
func init() { func init() {
x11Driver = newX11Window x11Driver = newX11Window
+6 -13
View File
@@ -142,8 +142,11 @@ func (w *Window) validateAndProcess(size image.Point, sync bool, frame *op.Ops,
if w.gpu == nil && !w.nocontext { if w.gpu == nil && !w.nocontext {
var err error var err error
if w.ctx == nil { if w.ctx == nil {
w.ctx, err = w.driver.NewContext() if w.ctx, err = w.driver.NewContext(); err != nil {
if err != nil { return err
}
if err = w.ctx.Lock(); err != nil {
w.destroyGPU()
return err return err
} }
sync = true sync = true
@@ -163,12 +166,6 @@ func (w *Window) validateAndProcess(size image.Point, sync bool, frame *op.Ops,
return err return err
} }
} }
if w.ctx != nil {
if err := w.ctx.Lock(); err != nil {
w.destroyGPU()
return err
}
}
if w.gpu == nil && !w.nocontext { if w.gpu == nil && !w.nocontext {
gpu, err := gpu.New(w.ctx.API()) gpu, err := gpu.New(w.ctx.API())
if err != nil { if err != nil {
@@ -200,7 +197,6 @@ func (w *Window) validateAndProcess(size image.Point, sync bool, frame *op.Ops,
var err error var err error
if w.gpu != nil { if w.gpu != nil {
err = w.ctx.Present() err = w.ctx.Present()
w.ctx.Unlock()
} }
return err return err
} }
@@ -444,10 +440,7 @@ func (c *callbacks) SetComposingRegion(r key.Range) {
func (c *callbacks) EditorInsert(text string) { func (c *callbacks) EditorInsert(text string) {
sel := c.w.imeState.Selection.Range sel := c.w.imeState.Selection.Range
c.EditorReplace(sel, text) c.EditorReplace(sel, text)
start := sel.Start start := min(sel.End, sel.Start)
if sel.End < start {
start = sel.End
}
sel.Start = start + utf8.RuneCountInString(text) sel.Start = start + utf8.RuneCountInString(text)
sel.End = sel.Start sel.End = sel.Start
c.SetEditorSelection(sel) c.SetEditorSelection(sel)
+10 -1
View File
@@ -30,6 +30,15 @@ func NewAffine2D(sx, hx, ox, hy, sy, oy float32) Affine2D {
} }
} }
// AffineId returns an identity transformation matrix that represents no transformation
// when applied.
func AffineId() Affine2D {
return NewAffine2D(
1, 0, 0,
0, 1, 0,
)
}
// Offset the transformation. // Offset the transformation.
func (a Affine2D) Offset(offset Point) Affine2D { func (a Affine2D) Offset(offset Point) Affine2D {
return Affine2D{ return Affine2D{
@@ -114,7 +123,7 @@ func (a Affine2D) Elems() (sx, hx, ox, hy, sy, oy float32) {
// Split a transform into two parts, one which is pure offset and the // Split a transform into two parts, one which is pure offset and the
// other representing the scaling, shearing and rotation part. // other representing the scaling, shearing and rotation part.
func (a *Affine2D) Split() (srs Affine2D, offset Point) { func (a Affine2D) Split() (srs Affine2D, offset Point) {
return Affine2D{ return Affine2D{
a: a.a, b: a.b, c: 0, a: a.a, b: a.b, c: 0,
d: a.d, e: a.e, f: 0, d: a.d, e: a.e, f: 0,
+160 -30
View File
@@ -27,11 +27,11 @@ func TestTransformOffset(t *testing.T) {
p := Point{X: 1, Y: 2} p := Point{X: 1, Y: 2}
o := Point{X: 2, Y: -3} o := Point{X: 2, Y: -3}
r := Affine2D{}.Offset(o).Transform(p) r := AffineId().Offset(o).Transform(p)
if !eq(r, Pt(3, -1)) { if !eq(r, Pt(3, -1)) {
t.Errorf("offset transformation mismatch: have %v, want {3 -1}", r) t.Errorf("offset transformation mismatch: have %v, want {3 -1}", r)
} }
i := Affine2D{}.Offset(o).Invert().Transform(r) i := AffineId().Offset(o).Invert().Transform(r)
if !eq(i, p) { if !eq(i, p) {
t.Errorf("offset transformation inverse mismatch: have %v, want %v", i, p) t.Errorf("offset transformation inverse mismatch: have %v, want %v", i, p)
} }
@@ -51,6 +51,9 @@ func TestString(t *testing.T) {
}, { }, {
in: NewAffine2D(29.142342, 31.4123412, 37.53152, 43.51324213, 47.123412, 53.14312342), in: NewAffine2D(29.142342, 31.4123412, 37.53152, 43.51324213, 47.123412, 53.14312342),
exp: "[[29.1423 31.4123 37.5315] [43.5132 47.1234 53.1431]]", exp: "[[29.1423 31.4123 37.5315] [43.5132 47.1234 53.1431]]",
}, {
in: AffineId(),
exp: "[[1 0 0] [0 1 0]]",
}, },
} }
for _, test := range tests { for _, test := range tests {
@@ -64,11 +67,11 @@ func TestTransformScale(t *testing.T) {
p := Point{X: 1, Y: 2} p := Point{X: 1, Y: 2}
s := Point{X: -1, Y: 2} s := Point{X: -1, Y: 2}
r := Affine2D{}.Scale(Point{}, s).Transform(p) r := AffineId().Scale(Point{}, s).Transform(p)
if !eq(r, Pt(-1, 4)) { if !eq(r, Pt(-1, 4)) {
t.Errorf("scale transformation mismatch: have %v, want {-1 4}", r) t.Errorf("scale transformation mismatch: have %v, want {-1 4}", r)
} }
i := Affine2D{}.Scale(Point{}, s).Invert().Transform(r) i := AffineId().Scale(Point{}, s).Invert().Transform(r)
if !eq(i, p) { if !eq(i, p) {
t.Errorf("scale transformation inverse mismatch: have %v, want %v", i, p) t.Errorf("scale transformation inverse mismatch: have %v, want %v", i, p)
} }
@@ -78,11 +81,11 @@ func TestTransformRotate(t *testing.T) {
p := Point{X: 1, Y: 0} p := Point{X: 1, Y: 0}
a := float32(math.Pi / 2) a := float32(math.Pi / 2)
r := Affine2D{}.Rotate(Point{}, a).Transform(p) r := AffineId().Rotate(Point{}, a).Transform(p)
if !eq(r, Pt(0, 1)) { if !eq(r, Pt(0, 1)) {
t.Errorf("rotate transformation mismatch: have %v, want {0 1}", r) t.Errorf("rotate transformation mismatch: have %v, want {0 1}", r)
} }
i := Affine2D{}.Rotate(Point{}, a).Invert().Transform(r) i := AffineId().Rotate(Point{}, a).Invert().Transform(r)
if !eq(i, p) { if !eq(i, p) {
t.Errorf("rotate transformation inverse mismatch: have %v, want %v", i, p) t.Errorf("rotate transformation inverse mismatch: have %v, want %v", i, p)
} }
@@ -91,11 +94,11 @@ func TestTransformRotate(t *testing.T) {
func TestTransformShear(t *testing.T) { func TestTransformShear(t *testing.T) {
p := Point{X: 1, Y: 1} p := Point{X: 1, Y: 1}
r := Affine2D{}.Shear(Point{}, math.Pi/4, 0).Transform(p) r := AffineId().Shear(Point{}, math.Pi/4, 0).Transform(p)
if !eq(r, Pt(2, 1)) { if !eq(r, Pt(2, 1)) {
t.Errorf("shear transformation mismatch: have %v, want {2 1}", r) t.Errorf("shear transformation mismatch: have %v, want {2 1}", r)
} }
i := Affine2D{}.Shear(Point{}, math.Pi/4, 0).Invert().Transform(r) i := AffineId().Shear(Point{}, math.Pi/4, 0).Invert().Transform(r)
if !eq(i, p) { if !eq(i, p) {
t.Errorf("shear transformation inverse mismatch: have %v, want %v", i, p) t.Errorf("shear transformation inverse mismatch: have %v, want %v", i, p)
} }
@@ -107,11 +110,11 @@ func TestTransformMultiply(t *testing.T) {
s := Point{X: -1, Y: 2} s := Point{X: -1, Y: 2}
a := float32(-math.Pi / 2) a := float32(-math.Pi / 2)
r := Affine2D{}.Offset(o).Scale(Point{}, s).Rotate(Point{}, a).Shear(Point{}, math.Pi/4, 0).Transform(p) r := AffineId().Offset(o).Scale(Point{}, s).Rotate(Point{}, a).Shear(Point{}, math.Pi/4, 0).Transform(p)
if !eq(r, Pt(1, 3)) { if !eq(r, Pt(1, 3)) {
t.Errorf("complex transformation mismatch: have %v, want {1 3}", r) t.Errorf("complex transformation mismatch: have %v, want {1 3}", r)
} }
i := Affine2D{}.Offset(o).Scale(Point{}, s).Rotate(Point{}, a).Shear(Point{}, math.Pi/4, 0).Invert().Transform(r) i := AffineId().Offset(o).Scale(Point{}, s).Rotate(Point{}, a).Shear(Point{}, math.Pi/4, 0).Invert().Transform(r)
if !eq(i, p) { if !eq(i, p) {
t.Errorf("complex transformation inverse mismatch: have %v, want %v", i, p) t.Errorf("complex transformation inverse mismatch: have %v, want %v", i, p)
} }
@@ -163,7 +166,7 @@ func TestPrimes(t *testing.T) {
func TestTransformScaleAround(t *testing.T) { func TestTransformScaleAround(t *testing.T) {
p := Pt(-1, -1) p := Pt(-1, -1)
target := Pt(-6, -13) target := Pt(-6, -13)
pt := Affine2D{}.Scale(Pt(4, 5), Pt(2, 3)).Transform(p) pt := AffineId().Scale(Pt(4, 5), Pt(2, 3)).Transform(p)
if !eq(pt, target) { if !eq(pt, target) {
t.Log(pt, "!=", target) t.Log(pt, "!=", target)
t.Error("Scale not as expected") t.Error("Scale not as expected")
@@ -172,7 +175,7 @@ func TestTransformScaleAround(t *testing.T) {
func TestTransformRotateAround(t *testing.T) { func TestTransformRotateAround(t *testing.T) {
p := Pt(-1, -1) p := Pt(-1, -1)
pt := Affine2D{}.Rotate(Pt(1, 1), -math.Pi/2).Transform(p) pt := AffineId().Rotate(Pt(1, 1), -math.Pi/2).Transform(p)
target := Pt(-1, 3) target := Pt(-1, 3)
if !eq(pt, target) { if !eq(pt, target) {
t.Log(pt, "!=", target) t.Log(pt, "!=", target)
@@ -181,12 +184,12 @@ func TestTransformRotateAround(t *testing.T) {
} }
func TestMulOrder(t *testing.T) { func TestMulOrder(t *testing.T) {
A := Affine2D{}.Offset(Pt(100, 100)) A := AffineId().Offset(Pt(100, 100))
B := Affine2D{}.Scale(Point{}, Pt(2, 2)) B := AffineId().Scale(Point{}, Pt(2, 2))
_ = A _ = A
_ = B _ = B
T1 := Affine2D{}.Offset(Pt(100, 100)).Scale(Point{}, Pt(2, 2)) T1 := AffineId().Offset(Pt(100, 100)).Scale(Point{}, Pt(2, 2))
T2 := B.Mul(A) T2 := B.Mul(A)
if T1 != T2 { if T1 != T2 {
@@ -199,9 +202,9 @@ func TestMulOrder(t *testing.T) {
func BenchmarkTransformOffset(b *testing.B) { func BenchmarkTransformOffset(b *testing.B) {
p := Point{X: 1, Y: 2} p := Point{X: 1, Y: 2}
o := Point{X: 0.5, Y: 0.5} o := Point{X: 0.5, Y: 0.5}
aff := Affine2D{}.Offset(o) aff := AffineId().Offset(o)
for i := 0; i < b.N; i++ { for b.Loop() {
p = aff.Transform(p) p = aff.Transform(p)
} }
_ = p _ = p
@@ -210,8 +213,8 @@ func BenchmarkTransformOffset(b *testing.B) {
func BenchmarkTransformScale(b *testing.B) { func BenchmarkTransformScale(b *testing.B) {
p := Point{X: 1, Y: 2} p := Point{X: 1, Y: 2}
s := Point{X: 0.5, Y: 0.5} s := Point{X: 0.5, Y: 0.5}
aff := Affine2D{}.Scale(Point{}, s) aff := AffineId().Scale(Point{}, s)
for i := 0; i < b.N; i++ { for b.Loop() {
p = aff.Transform(p) p = aff.Transform(p)
} }
_ = p _ = p
@@ -220,36 +223,163 @@ func BenchmarkTransformScale(b *testing.B) {
func BenchmarkTransformRotate(b *testing.B) { func BenchmarkTransformRotate(b *testing.B) {
p := Point{X: 1, Y: 2} p := Point{X: 1, Y: 2}
a := float32(math.Pi / 2) a := float32(math.Pi / 2)
aff := Affine2D{}.Rotate(Point{}, a) aff := AffineId().Rotate(Point{}, a)
for i := 0; i < b.N; i++ { for b.Loop() {
p = aff.Transform(p) p = aff.Transform(p)
} }
_ = p _ = p
} }
func BenchmarkTransformTranslateMultiply(b *testing.B) { func BenchmarkTransformTranslateMultiply(b *testing.B) {
a := Affine2D{}.Offset(Point{X: 1, Y: 1}).Rotate(Point{}, math.Pi/3) a := AffineId().Offset(Point{X: 1, Y: 1}).Rotate(Point{}, math.Pi/3)
t := Affine2D{}.Offset(Point{X: 0.5, Y: 0.5}) t := AffineId().Offset(Point{X: 0.5, Y: 0.5})
for i := 0; i < b.N; i++ { for b.Loop() {
a = a.Mul(t) a = a.Mul(t)
} }
} }
func BenchmarkTransformScaleMultiply(b *testing.B) { func BenchmarkTransformScaleMultiply(b *testing.B) {
a := Affine2D{}.Offset(Point{X: 1, Y: 1}).Rotate(Point{}, math.Pi/3) a := AffineId().Offset(Point{X: 1, Y: 1}).Rotate(Point{}, math.Pi/3)
t := Affine2D{}.Offset(Point{X: 0.5, Y: 0.5}).Scale(Point{}, Point{X: 0.4, Y: -0.5}) t := AffineId().Offset(Point{X: 0.5, Y: 0.5}).Scale(Point{}, Point{X: 0.4, Y: -0.5})
for i := 0; i < b.N; i++ { for b.Loop() {
a = a.Mul(t) a = a.Mul(t)
} }
} }
func BenchmarkTransformMultiply(b *testing.B) { func BenchmarkTransformMultiply(b *testing.B) {
a := Affine2D{}.Offset(Point{X: 1, Y: 1}).Rotate(Point{}, math.Pi/3) a := AffineId().Offset(Point{X: 1, Y: 1}).Rotate(Point{}, math.Pi/3)
t := Affine2D{}.Offset(Point{X: 0.5, Y: 0.5}).Rotate(Point{}, math.Pi/7) t := AffineId().Offset(Point{X: 0.5, Y: 0.5}).Rotate(Point{}, math.Pi/7)
for i := 0; i < b.N; i++ { for b.Loop() {
a = a.Mul(t) a = a.Mul(t)
} }
} }
func TestNewAffine2D(t *testing.T) {
tests := []struct {
sx, hx, ox, hy, sy, oy float32
expected Affine2D
}{
{1, 0, 0, 0, 1, 0, AffineId()},
{2, 0, 5, 0, 3, 7, Affine2D{a: 1, b: 0, c: 5, d: 0, e: 2, f: 7}},
{-1, 2, 3, 4, -5, 6, Affine2D{a: -2, b: 2, c: 3, d: 4, e: -6, f: 6}},
}
for i, test := range tests {
got := NewAffine2D(test.sx, test.hx, test.ox, test.hy, test.sy, test.oy)
if !eqaff(got, test.expected) {
t.Errorf(
"Test %d: NewAffine2D(%v, %v, %v, %v, %v, %v) = %v, want %v",
i, test.sx, test.hx, test.ox, test.hy, test.sy, test.oy, got, test.expected,
)
}
}
}
func TestAffineId(t *testing.T) {
id := AffineId()
testPoints := []Point{
{0, 0},
{1, 0},
{0, 1},
{-1, -1},
{10, 20},
}
for _, p := range testPoints {
transformed := id.Transform(p)
if !eq(transformed, p) {
t.Errorf("Identity transform changed point: %v -> %v", p, transformed)
}
}
}
func TestElems(t *testing.T) {
tests := []struct {
aff Affine2D
sx, hx, ox, hy, sy, oy float32
}{
{AffineId(), 1, 0, 0, 0, 1, 0},
{Affine2D{a: 1, b: 2, c: 3, d: 4, e: 5, f: 6}, 2, 2, 3, 4, 6, 6},
{NewAffine2D(7, 8, 9, 10, 11, 12), 7, 8, 9, 10, 11, 12},
}
for i, test := range tests {
sx, hx, ox, hy, sy, oy := test.aff.Elems()
if sx != test.sx || hx != test.hx || ox != test.ox ||
hy != test.hy || sy != test.sy || oy != test.oy {
t.Errorf(
"Test %d: %v.Elems() = (%v, %v, %v, %v, %v, %v), want (%v, %v, %v, %v, %v, %v)",
i, test.aff, sx, hx, ox, hy, sy, oy, test.sx, test.hx, test.ox, test.hy, test.sy, test.oy,
)
}
}
}
func TestSplit(t *testing.T) {
tests := []struct {
aff Affine2D
expectedSRS Affine2D
expectedOffset Point
}{
{
AffineId(),
AffineId(),
Point{0, 0},
},
{
Affine2D{a: 1, b: 2, c: 3, d: 4, e: 5, f: 6},
Affine2D{a: 1, b: 2, c: 0, d: 4, e: 5, f: 0},
Point{3, 6},
},
{
NewAffine2D(2, 0, 10, 0, 3, 20),
NewAffine2D(2, 0, 0, 0, 3, 0),
Point{10, 20},
},
}
for i, test := range tests {
srs, offset := test.aff.Split()
if !eqaff(srs, test.expectedSRS) || !eq(offset, test.expectedOffset) {
t.Errorf(
"Test %d: %v.Split() = (%v, %v), want (%v, %v)",
i, test.aff, srs, offset, test.expectedSRS, test.expectedOffset,
)
}
}
}
func TestShear(t *testing.T) {
p := Pt(2, 3)
origin := Pt(1, 1)
shearX := AffineId().Shear(origin, math.Pi/4, 0)
resultX := shearX.Transform(p)
expectedX := Pt(4, 3)
if !eq(resultX, expectedX) {
t.Errorf("Shear around origin in X: got %v, want %v", resultX, expectedX)
}
inverseX := shearX.Invert().Transform(resultX)
if !eq(inverseX, p) {
t.Errorf("Inverse shear X: got %v, want %v", inverseX, p)
}
shearY := AffineId().Shear(origin, 0, math.Pi/4)
resultY := shearY.Transform(p)
expectedY := Pt(2, 4)
if !eq(resultY, expectedY) {
t.Errorf("Shear around origin in Y: got %v, want %v", resultY, expectedY)
}
inverseY := shearY.Invert().Transform(resultY)
if !eq(inverseY, p) {
t.Errorf("Inverse shear Y: got %v, want %v", inverseY, p)
}
}
Generated
+24 -67
View File
@@ -1,86 +1,25 @@
{ {
"nodes": { "nodes": {
"android": {
"inputs": {
"devshell": "devshell",
"flake-utils": "flake-utils",
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1733430059,
"narHash": "sha256-o3O5tjrMMebRLuHQt7BbEw3jZgWRW5vnOptNXv8WdO4=",
"owner": "tadfisher",
"repo": "android-nixpkgs",
"rev": "d2f3c1ea99c0bea9d28a0e59daeb482f50d4cd35",
"type": "github"
},
"original": {
"owner": "tadfisher",
"repo": "android-nixpkgs",
"type": "github"
}
},
"devshell": {
"inputs": {
"nixpkgs": [
"android",
"nixpkgs"
]
},
"locked": {
"lastModified": 1728330715,
"narHash": "sha256-xRJ2nPOXb//u1jaBnDP56M7v5ldavjbtR6lfGqSvcKg=",
"owner": "numtide",
"repo": "devshell",
"rev": "dd6b80932022cea34a019e2bb32f6fa9e494dfef",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "devshell",
"type": "github"
}
},
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1731533236,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1733261153, "lastModified": 1747953325,
"narHash": "sha256-eq51hyiaIwtWo19fPEeE0Zr2s83DYMKJoukNLgGGpek=", "narHash": "sha256-y2ZtlIlNTuVJUZCqzZAhIw5rrKP4DOSklev6c8PyCkQ=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "b681065d0919f7eb5309a93cea2cfa84dec9aa88", "rev": "55d1f923c480dadce40f5231feb472e81b0bab48",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "NixOS", "owner": "NixOS",
"ref": "nixos-24.11", "ref": "nixos-25.05",
"repo": "nixpkgs", "repo": "nixpkgs",
"type": "github" "type": "github"
} }
}, },
"root": { "root": {
"inputs": { "inputs": {
"android": "android", "nixpkgs": "nixpkgs",
"nixpkgs": "nixpkgs" "utils": "utils"
} }
}, },
"systems": { "systems": {
@@ -97,6 +36,24 @@
"repo": "default", "repo": "default",
"type": "github" "type": "github"
} }
},
"utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1731533236,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
} }
}, },
"root": "root", "root": "root",
+37 -40
View File
@@ -3,42 +3,38 @@
description = "Gio build environment"; description = "Gio build environment";
inputs = { inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.11"; nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.05";
android.url = "github:tadfisher/android-nixpkgs"; utils.url = "github:numtide/flake-utils";
android.inputs.nixpkgs.follows = "nixpkgs";
}; };
outputs = { self, nixpkgs, android }: outputs = { self, nixpkgs, utils }:
let utils.lib.eachDefaultSystem (system:
supportedSystems = [ "x86_64-linux" "x86_64-darwin" "aarch64-darwin" ]; let
forAllSystems = f: nixpkgs.lib.genAttrs supportedSystems (system: f system); pkgs = import nixpkgs {
in inherit system;
{
devShells = forAllSystems # allow unfree Android packages.
(system: config.allowUnfree = true;
let # accept the Android SDK license.
pkgs = import nixpkgs { config.android_sdk.accept_license = true;
inherit system; };
in {
devShells = let
android-sdk = let
androidComposition = pkgs.androidenv.composeAndroidPackages {
platformVersions = [ "latest" ];
abiVersions = [ "armeabi-v7a" "arm64-v8a" ];
# Omit the deprecated tools package.
toolsVersion = null;
includeNDK = true;
}; };
android-sdk = android.sdk.${system} (sdkPkgs: with sdkPkgs; in androidComposition.androidsdk;
[ in {
build-tools-31-0-0 default = with pkgs;
cmdline-tools-latest mkShell (rec {
platform-tools ANDROID_HOME = "${android-sdk}/libexec/android-sdk";
platforms-android-31 packages = [ android-sdk jdk clang ]
ndk-bundle ++ (if stdenv.isLinux then [
]);
in
{
default = with pkgs; mkShell
({
ANDROID_SDK_ROOT = "${android-sdk}/share/android-sdk";
JAVA_HOME = jdk17.home;
packages = [
android-sdk
jdk17
clang
] ++ (if stdenv.isLinux then [
vulkan-headers vulkan-headers
libxkbcommon libxkbcommon
wayland wayland
@@ -47,11 +43,12 @@
xorg.libXfixes xorg.libXfixes
libGL libGL
pkg-config pkg-config
] else [ ]); ] else
} // (if stdenv.isLinux then { [ ]);
LD_LIBRARY_PATH = "${vulkan-loader}/lib"; } // (if stdenv.isLinux then {
} else { })); LD_LIBRARY_PATH = "${vulkan-loader}/lib";
} } else
); { }));
}; };
});
} }
+9 -2
View File
@@ -322,6 +322,8 @@ func (s *Scroll) Update(cfg unit.Metric, q input.Source, t time.Time, axis Axis,
s.scroll += e.Scroll.X s.scroll += e.Scroll.X
case Vertical: case Vertical:
s.scroll += e.Scroll.Y s.scroll += e.Scroll.Y
case Both:
s.scroll += e.Scroll.X + e.Scroll.Y
} }
iscroll := int(s.scroll) iscroll := int(s.scroll)
s.scroll -= float32(iscroll) s.scroll -= float32(iscroll)
@@ -353,10 +355,15 @@ func (s *Scroll) Update(cfg unit.Metric, q input.Source, t time.Time, axis Axis,
} }
func (s *Scroll) val(axis Axis, p f32.Point) float32 { func (s *Scroll) val(axis Axis, p f32.Point) float32 {
if axis == Horizontal { switch axis {
case Horizontal:
return p.X return p.X
} else { case Vertical:
return p.Y return p.Y
case Both:
return p.X + p.Y
default:
return 0
} }
} }
+7 -7
View File
@@ -1,14 +1,14 @@
module gioui.org module gioui.org
go 1.21 go 1.23.8
require ( require (
eliasnaur.com/font v0.0.0-20230308162249-dd43949cb42d eliasnaur.com/font v0.0.0-20230308162249-dd43949cb42d
gioui.org/shader v1.0.8 gioui.org/shader v1.0.8
github.com/go-text/typesetting v0.2.1 github.com/go-text/typesetting v0.3.0
golang.org/x/exp v0.0.0-20240707233637-46b078467d37 golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0
golang.org/x/exp/shiny v0.0.0-20240707233637-46b078467d37 golang.org/x/exp/shiny v0.0.0-20250408133849-7e4ce0ab07d0
golang.org/x/image v0.18.0 golang.org/x/image v0.26.0
golang.org/x/sys v0.22.0 golang.org/x/sys v0.33.0
golang.org/x/text v0.16.0 golang.org/x/text v0.24.0
) )
+12 -12
View File
@@ -3,17 +3,17 @@ eliasnaur.com/font v0.0.0-20230308162249-dd43949cb42d/go.mod h1:OYVuxibdk9OSLX8v
gioui.org/cpu v0.0.0-20210808092351-bfe733dd3334/go.mod h1:A8M0Cn5o+vY5LTMlnRoK3O5kG+rH0kWfJjeKd9QpBmQ= gioui.org/cpu v0.0.0-20210808092351-bfe733dd3334/go.mod h1:A8M0Cn5o+vY5LTMlnRoK3O5kG+rH0kWfJjeKd9QpBmQ=
gioui.org/shader v1.0.8 h1:6ks0o/A+b0ne7RzEqRZK5f4Gboz2CfG+mVliciy6+qA= gioui.org/shader v1.0.8 h1:6ks0o/A+b0ne7RzEqRZK5f4Gboz2CfG+mVliciy6+qA=
gioui.org/shader v1.0.8/go.mod h1:mWdiME581d/kV7/iEhLmUgUK5iZ09XR5XpduXzbePVM= gioui.org/shader v1.0.8/go.mod h1:mWdiME581d/kV7/iEhLmUgUK5iZ09XR5XpduXzbePVM=
github.com/go-text/typesetting v0.2.1 h1:x0jMOGyO3d1qFAPI0j4GSsh7M0Q3Ypjzr4+CEVg82V8= github.com/go-text/typesetting v0.3.0 h1:OWCgYpp8njoxSRpwrdd1bQOxdjOXDj9Rqart9ML4iF4=
github.com/go-text/typesetting v0.2.1/go.mod h1:mTOxEwasOFpAMBjEQDhdWRckoLLeI/+qrQeBCTGEt6M= github.com/go-text/typesetting v0.3.0/go.mod h1:qjZLkhRgOEYMhU9eHBr3AR4sfnGJvOXNLt8yRAySFuY=
github.com/go-text/typesetting-utils v0.0.0-20241103174707-87a29e9e6066 h1:qCuYC+94v2xrb1PoS4NIDe7DGYtLnU2wWiQe9a1B1c0= github.com/go-text/typesetting-utils v0.0.0-20241103174707-87a29e9e6066 h1:qCuYC+94v2xrb1PoS4NIDe7DGYtLnU2wWiQe9a1B1c0=
github.com/go-text/typesetting-utils v0.0.0-20241103174707-87a29e9e6066/go.mod h1:DDxDdQEnB70R8owOx3LVpEFvpMK9eeH1o2r0yZhFI9o= github.com/go-text/typesetting-utils v0.0.0-20241103174707-87a29e9e6066/go.mod h1:DDxDdQEnB70R8owOx3LVpEFvpMK9eeH1o2r0yZhFI9o=
golang.org/x/exp v0.0.0-20240707233637-46b078467d37 h1:uLDX+AfeFCct3a2C7uIWBKMJIR3CJMhcgfrUAqjRK6w= golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 h1:R84qjqJb5nVJMxqWYb3np9L5ZsaDtB+a39EqjV0JSUM=
golang.org/x/exp v0.0.0-20240707233637-46b078467d37/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:S9Xr4PYopiDyqSyp5NjCrhFrqg6A5zA2E/iPHPhqnS8=
golang.org/x/exp/shiny v0.0.0-20240707233637-46b078467d37 h1:SOSg7+sueresE4IbmmGM60GmlIys+zNX63d6/J4CMtU= golang.org/x/exp/shiny v0.0.0-20250408133849-7e4ce0ab07d0 h1:tMSqXTK+AQdW3LpCbfatHSRPHeW6+2WuxaVQuHftn80=
golang.org/x/exp/shiny v0.0.0-20240707233637-46b078467d37/go.mod h1:3F+MieQB7dRYLTmnncoFbb1crS5lfQoTfDgQy6K4N0o= golang.org/x/exp/shiny v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:ygj7T6vSGhhm/9yTpOQQNvuAUFziTH7RUiH74EoE2C8=
golang.org/x/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ= golang.org/x/image v0.26.0 h1:4XjIFEZWQmCZi6Wv8BoxsDhRU3RVnLX04dToTDAEPlY=
golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E= golang.org/x/image v0.26.0/go.mod h1:lcxbMFAovzpnJxzXS3nyL83K27tmqtKzIJpctK8YO5c=
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
+2 -3
View File
@@ -4,8 +4,7 @@ package gpu
import ( import (
"fmt" "fmt"
"image"
"gioui.org/internal/f32"
) )
type textureCacheKey struct { type textureCacheKey struct {
@@ -36,7 +35,7 @@ type opCache struct {
type opCacheValue struct { type opCacheValue struct {
data pathData data pathData
bounds f32.Rectangle bounds image.Rectangle
// the fields below are handled by opCache // the fields below are handled by opCache
key opKey key opKey
keep bool keep bool
+6 -6
View File
@@ -19,10 +19,10 @@ type quadSplitter struct {
func encodeQuadTo(data []byte, meta uint32, from, ctrl, to f32.Point) { func encodeQuadTo(data []byte, meta uint32, from, ctrl, to f32.Point) {
// inlined code: // inlined code:
// encodeVertex(data, meta, -1, 1, from, ctrl, to) // encodeVertex(data, meta, 1, -1, from, ctrl, to)
// encodeVertex(data[vertStride:], meta, 1, 1, from, ctrl, to) // encodeVertex(data[vertStride:], meta, 1, 1, from, ctrl, to)
// encodeVertex(data[vertStride*2:], meta, -1, -1, from, ctrl, to) // encodeVertex(data[vertStride*2:], meta, -1, -1, from, ctrl, to)
// encodeVertex(data[vertStride*3:], meta, 1, -1, from, ctrl, to) // encodeVertex(data[vertStride*3:], meta, -1, 1, from, ctrl, to)
// this code needs to stay in sync with `vertex.encode`. // this code needs to stay in sync with `vertex.encode`.
bo := binary.LittleEndian bo := binary.LittleEndian
@@ -48,10 +48,10 @@ func encodeQuadTo(data []byte, meta uint32, from, ctrl, to f32.Point) {
} }
const ( const (
nwCorner = 1*0.25 + 0*0.5 nwCorner = 1*0.5 + 0*0.25
neCorner = 1*0.25 + 1*0.5 neCorner = 1*0.5 + 1*0.25
swCorner = 0*0.25 + 0*0.5 swCorner = 0*0.5 + 0*0.25
seCorner = 0*0.25 + 1*0.5 seCorner = 0*0.5 + 1*0.25
) )
func encodeVertex(data []byte, meta uint32, cornerx, cornery int16, from, ctrl, to f32.Point) { func encodeVertex(data []byte, meta uint32, cornerx, cornery int16, from, ctrl, to f32.Point) {
+1 -1
View File
@@ -10,7 +10,7 @@ import (
func BenchmarkEncodeQuadTo(b *testing.B) { func BenchmarkEncodeQuadTo(b *testing.B) {
var data [vertStride * 4]byte var data [vertStride * 4]byte
for i := 0; i < b.N; i++ { for i := 0; b.Loop(); i++ {
v := float32(i) v := float32(i)
encodeQuadTo(data[:], 123, encodeQuadTo(data[:], 123,
f32.Point{X: v, Y: v}, f32.Point{X: v, Y: v},
+86 -75
View File
@@ -15,6 +15,7 @@ import (
"image/color" "image/color"
"math" "math"
"reflect" "reflect"
"slices"
"time" "time"
"unsafe" "unsafe"
@@ -117,17 +118,17 @@ type drawState struct {
} }
type pathOp struct { type pathOp struct {
off f32.Point off image.Point
// rect tracks whether the clip stack can be represented by a // rect tracks whether the clip stack can be represented by a
// pixel-aligned rectangle. // pixel-aligned rectangle.
rect bool rect bool
// clip is the union of all // clip is the union of all
// later clip rectangles. // later clip rectangles.
clip image.Rectangle clip image.Rectangle
bounds f32.Rectangle bounds image.Rectangle
// intersect is the intersection of bounds and all // intersect is the intersection of bounds and all
// previous clip bounds. // previous clip bounds.
intersect f32.Rectangle intersect image.Rectangle
pathKey opKey pathKey opKey
path bool path bool
pathVerts []byte pathVerts []byte
@@ -189,7 +190,7 @@ const (
// imageOpData is the shadow of paint.ImageOp. // imageOpData is the shadow of paint.ImageOp.
type imageOpData struct { type imageOpData struct {
src *image.RGBA src *image.RGBA
handle interface{} handle any
filter byte filter byte
} }
@@ -200,7 +201,7 @@ type linearGradientOpData struct {
color2 color.NRGBA color2 color.NRGBA
} }
func decodeImageOp(data []byte, refs []interface{}) imageOpData { func decodeImageOp(data []byte, refs []any) imageOpData {
handle := refs[1] handle := refs[1]
if handle == nil { if handle == nil {
return imageOpData{} return imageOpData{}
@@ -547,7 +548,7 @@ func newBlitter(ctx driver.Device) *blitter {
b.texUniforms = new(blitTexUniforms) b.texUniforms = new(blitTexUniforms)
b.linearGradientUniforms = new(blitLinearGradientUniforms) b.linearGradientUniforms = new(blitLinearGradientUniforms)
pipelines, err := createColorPrograms(ctx, gio.Shader_blit_vert, gio.Shader_blit_frag, pipelines, err := createColorPrograms(ctx, gio.Shader_blit_vert, gio.Shader_blit_frag,
[3]interface{}{b.colUniforms, b.linearGradientUniforms, b.texUniforms}, [3]any{b.colUniforms, b.linearGradientUniforms, b.texUniforms},
) )
if err != nil { if err != nil {
panic(err) panic(err)
@@ -565,7 +566,7 @@ func (b *blitter) release() {
} }
} }
func createColorPrograms(b driver.Device, vsSrc shader.Sources, fsSrc [3]shader.Sources, uniforms [3]interface{}) (pipelines [2][3]*pipeline, err error) { func createColorPrograms(b driver.Device, vsSrc shader.Sources, fsSrc [3]shader.Sources, uniforms [3]any) (pipelines [2][3]*pipeline, err error) {
defer func() { defer func() {
if err != nil { if err != nil {
for _, p := range pipelines { for _, p := range pipelines {
@@ -822,7 +823,7 @@ func (r *renderer) packLayers(layers []opacityLayer) []opacityLayer {
layers[l.parent].clip = b.Union(l.clip) layers[l.parent].clip = b.Union(l.clip)
} }
if l.clip.Empty() { if l.clip.Empty() {
layers = append(layers[:i], layers[i+1:]...) layers = slices.Delete(layers, i, i+1)
} }
} }
// Pack layers. // Pack layers.
@@ -871,7 +872,7 @@ func (r *renderer) drawLayers(layers []opacityLayer, ops []imageOp) {
r.drawOps(true, l.clip.Min.Mul(-1), l.clip.Size(), ops[l.opStart:l.opEnd]) r.drawOps(true, l.clip.Min.Mul(-1), l.clip.Size(), ops[l.opStart:l.opEnd])
sr := f32.FRect(v) sr := f32.FRect(v)
uvScale, uvOffset := texSpaceTransform(sr, f.size) uvScale, uvOffset := texSpaceTransform(sr, f.size)
uvTrans := f32.Affine2D{}.Scale(f32.Point{}, uvScale).Offset(uvOffset) uvTrans := f32.AffineId().Scale(f32.Point{}, uvScale).Offset(uvOffset)
// Replace layer ops with one textured op. // Replace layer ops with one textured op.
ops[l.opStart] = imageOp{ ops[l.opStart] = imageOp{
clip: l.clip, clip: l.clip,
@@ -901,16 +902,14 @@ func (d *drawOps) reset(viewport image.Point) {
d.opacityStack = d.opacityStack[:0] d.opacityStack = d.opacityStack[:0]
} }
func (d *drawOps) collect(root *op.Ops, viewport image.Point) { func (d *drawOps) collect(root *op.Ops, viewportSize image.Point) {
viewf := f32.Rectangle{ viewport := image.Rectangle{Max: viewportSize}
Max: f32.Point{X: float32(viewport.X), Y: float32(viewport.Y)},
}
var ops *ops.Ops var ops *ops.Ops
if root != nil { if root != nil {
ops = &root.Internal ops = &root.Internal
} }
d.reader.Reset(ops) d.reader.Reset(ops)
d.collectOps(&d.reader, viewf) d.collectOps(&d.reader, viewport)
} }
func (d *drawOps) buildPaths(ctx driver.Device) { func (d *drawOps) buildPaths(ctx driver.Device) {
@@ -931,7 +930,7 @@ func (d *drawOps) newPathOp() *pathOp {
return &d.pathOpCache[len(d.pathOpCache)-1] return &d.pathOpCache[len(d.pathOpCache)-1]
} }
func (d *drawOps) addClipPath(state *drawState, aux []byte, auxKey opKey, bounds f32.Rectangle, off f32.Point) { func (d *drawOps) addClipPath(state *drawState, aux []byte, auxKey opKey, bounds image.Rectangle, off image.Point) {
npath := d.newPathOp() npath := d.newPathOp()
*npath = pathOp{ *npath = pathOp{
parent: state.cpath, parent: state.cpath,
@@ -956,7 +955,9 @@ func (d *drawOps) addClipPath(state *drawState, aux []byte, auxKey opKey, bounds
func (d *drawOps) save(id int, state f32.Affine2D) { func (d *drawOps) save(id int, state f32.Affine2D) {
if extra := id - len(d.states) + 1; extra > 0 { if extra := id - len(d.states) + 1; extra > 0 {
d.states = append(d.states, make([]f32.Affine2D, extra)...) for range extra {
d.states = append(d.states, f32.AffineId())
}
} }
d.states[id] = state d.states[id] = state
} }
@@ -970,13 +971,14 @@ func (k opKey) SetTransform(t f32.Affine2D) opKey {
return k return k
} }
func (d *drawOps) collectOps(r *ops.Reader, viewport f32.Rectangle) { func (d *drawOps) collectOps(r *ops.Reader, viewport image.Rectangle) {
var ( var quads quadsOp
quads quadsOp state := drawState{
state drawState t: f32.AffineId(),
) }
reset := func() { reset := func() {
state = drawState{ state = drawState{
t: f32.AffineId(),
color: color.NRGBA{A: 0xff}, color: color.NRGBA{A: 0xff},
} }
} }
@@ -1031,8 +1033,8 @@ loop:
var op ops.ClipOp var op ops.ClipOp
op.Decode(encOp.Data) op.Decode(encOp.Data)
quads.key.outline = op.Outline quads.key.outline = op.Outline
bounds := f32.FRect(op.Bounds) bounds := op.Bounds
trans, off := state.t.Split() trans, off := transformOffset(state.t)
if len(quads.aux) > 0 { if len(quads.aux) > 0 {
// There is a clipping path, build the gpu data and update the // There is a clipping path, build the gpu data and update the
// cache key such that it will be equal only if the transform is the // cache key such that it will be equal only if the transform is the
@@ -1043,11 +1045,11 @@ loop:
// Why is this not used for the offset shapes? // Why is this not used for the offset shapes?
bounds = v.bounds bounds = v.bounds
} else { } else {
var pathData []byte newPathData, newBounds := d.buildVerts(
pathData, bounds = d.buildVerts(
quads.aux, trans, quads.key.outline, quads.key.strokeWidth, quads.aux, trans, quads.key.outline, quads.key.strokeWidth,
) )
quads.aux = pathData quads.aux = newPathData
bounds = newBounds.Round()
// add it to the cache, without GPU data, so the transform can be // add it to the cache, without GPU data, so the transform can be
// reused. // reused.
d.pathCache.put(quads.key, opCacheValue{bounds: bounds}) d.pathCache.put(quads.key, opCacheValue{bounds: bounds})
@@ -1055,6 +1057,7 @@ loop:
} else { } else {
quads.aux, bounds, _ = d.boundsForTransformedRect(bounds, trans) quads.aux, bounds, _ = d.boundsForTransformedRect(bounds, trans)
quads.key = opKey{Key: encOp.Key} quads.key = opKey{Key: encOp.Key}
quads.key = quads.key.SetTransform(trans)
} }
d.addClipPath(&state, quads.aux, quads.key, bounds, off) d.addClipPath(&state, quads.aux, quads.key, bounds, off)
quads = quadsOp{} quads = quadsOp{}
@@ -1078,21 +1081,21 @@ loop:
// Transform (if needed) the painting rectangle and if so generate a clip path, // Transform (if needed) the painting rectangle and if so generate a clip path,
// for those cases also compute a partialTrans that maps texture coordinates between // for those cases also compute a partialTrans that maps texture coordinates between
// the new bounding rectangle and the transformed original paint rectangle. // the new bounding rectangle and the transformed original paint rectangle.
t, off := state.t.Split() t, off := transformOffset(state.t)
// Fill the clip area, unless the material is a (bounded) image. // Fill the clip area, unless the material is a (bounded) image.
// TODO: Find a tighter bound. // TODO: Find a tighter bound.
inf := float32(1e6) inf := int(1e6)
dst := f32.Rect(-inf, -inf, inf, inf) dst := image.Rect(-inf, -inf, inf, inf)
if state.matType == materialTexture { if state.matType == materialTexture {
sz := state.image.src.Rect.Size() sz := state.image.src.Rect.Size()
dst = f32.Rectangle{Max: layout.FPt(sz)} dst = image.Rectangle{Max: sz}
} }
clipData, bnd, partialTrans := d.boundsForTransformedRect(dst, t) clipData, bnd, partialTrans := d.boundsForTransformedRect(dst, t)
cl := viewport.Intersect(bnd.Add(off)) bounds := viewport.Intersect(bnd.Add(off))
if state.cpath != nil { if state.cpath != nil {
cl = state.cpath.intersect.Intersect(cl) bounds = state.cpath.intersect.Intersect(bounds)
} }
if cl.Empty() { if bounds.Empty() {
continue continue
} }
@@ -1100,11 +1103,10 @@ loop:
// The paint operation is sheared or rotated, add a clip path representing // The paint operation is sheared or rotated, add a clip path representing
// this transformed rectangle. // this transformed rectangle.
k := opKey{Key: encOp.Key} k := opKey{Key: encOp.Key}
k.SetTransform(t) // TODO: This call has no effect. k = k.SetTransform(t)
d.addClipPath(&state, clipData, k, bnd, off) d.addClipPath(&state, clipData, k, bnd, off)
} }
bounds := cl.Round()
mat := state.materialFor(bnd, off, partialTrans, bounds) mat := state.materialFor(bnd, off, partialTrans, bounds)
rect := state.cpath == nil || state.cpath.rect rect := state.cpath == nil || state.cpath.rect
@@ -1158,9 +1160,10 @@ func expandPathOp(p *pathOp, clip image.Rectangle) {
} }
} }
func (d *drawState) materialFor(rect f32.Rectangle, off f32.Point, partTrans f32.Affine2D, clip image.Rectangle) material { func (d *drawState) materialFor(rect image.Rectangle, off image.Point, partTrans f32.Affine2D, clip image.Rectangle) material {
m := material{ m := material{
opacity: 1., opacity: 1.,
uvTrans: f32.AffineId(),
} }
switch d.matType { switch d.matType {
case materialColor: case materialColor:
@@ -1177,7 +1180,7 @@ func (d *drawState) materialFor(rect f32.Rectangle, off f32.Point, partTrans f32
m.uvTrans = partTrans.Mul(gradientSpaceTransform(clip, off, d.stop1, d.stop2)) m.uvTrans = partTrans.Mul(gradientSpaceTransform(clip, off, d.stop1, d.stop2))
case materialTexture: case materialTexture:
m.material = materialTexture m.material = materialTexture
dr := rect.Add(off).Round() dr := rect.Add(off)
sz := d.image.src.Bounds().Size() sz := d.image.src.Bounds().Size()
sr := f32.Rectangle{ sr := f32.Rectangle{
Max: f32.Point{ Max: f32.Point{
@@ -1194,7 +1197,7 @@ func (d *drawState) materialFor(rect f32.Rectangle, off f32.Point, partTrans f32
sr.Min.Y += float32(clip.Min.Y-dr.Min.Y) * sdy / dy sr.Min.Y += float32(clip.Min.Y-dr.Min.Y) * sdy / dy
sr.Max.Y -= float32(dr.Max.Y-clip.Max.Y) * sdy / dy sr.Max.Y -= float32(dr.Max.Y-clip.Max.Y) * sdy / dy
uvScale, uvOffset := texSpaceTransform(sr, sz) uvScale, uvOffset := texSpaceTransform(sr, sz)
m.uvTrans = partTrans.Mul(f32.Affine2D{}.Scale(f32.Point{}, uvScale).Offset(uvOffset)) m.uvTrans = partTrans.Mul(f32.AffineId().Scale(f32.Point{}, uvScale).Offset(uvOffset))
m.data = d.image m.data = d.image
} }
return m return m
@@ -1315,7 +1318,7 @@ func (b *blitter) blit(mat materialType, fbo bool, col f32color.RGBA, col1, col2
// newUniformBuffer creates a new GPU uniform buffer backed by the // newUniformBuffer creates a new GPU uniform buffer backed by the
// structure uniformBlock points to. // structure uniformBlock points to.
func newUniformBuffer(b driver.Device, uniformBlock interface{}) *uniformBuffer { func newUniformBuffer(b driver.Device, uniformBlock any) *uniformBuffer {
ref := reflect.ValueOf(uniformBlock) ref := reflect.ValueOf(uniformBlock)
// Determine the size of the uniforms structure, *uniforms. // Determine the size of the uniforms structure, *uniforms.
size := ref.Elem().Type().Size() size := ref.Elem().Type().Size()
@@ -1362,19 +1365,19 @@ func texSpaceTransform(r f32.Rectangle, bounds image.Point) (f32.Point, f32.Poin
} }
// gradientSpaceTransform transforms stop1 and stop2 to [(0,0), (1,1)]. // gradientSpaceTransform transforms stop1 and stop2 to [(0,0), (1,1)].
func gradientSpaceTransform(clip image.Rectangle, off f32.Point, stop1, stop2 f32.Point) f32.Affine2D { func gradientSpaceTransform(clip image.Rectangle, off image.Point, stop1, stop2 f32.Point) f32.Affine2D {
d := stop2.Sub(stop1) d := stop2.Sub(stop1)
l := float32(math.Sqrt(float64(d.X*d.X + d.Y*d.Y))) l := float32(math.Sqrt(float64(d.X*d.X + d.Y*d.Y)))
a := float32(math.Atan2(float64(-d.Y), float64(d.X))) a := float32(math.Atan2(float64(-d.Y), float64(d.X)))
// TODO: optimize // TODO: optimize
zp := f32.Point{} zp := f32.Point{}
return f32.Affine2D{}. return f32.AffineId().
Scale(zp, layout.FPt(clip.Size())). // scale to pixel space Scale(zp, layout.FPt(clip.Size())). // scale to pixel space
Offset(zp.Sub(off).Add(layout.FPt(clip.Min))). // offset to clip space Offset(zp.Sub(f32.FPt(off)).Add(layout.FPt(clip.Min))). // offset to clip space
Offset(zp.Sub(stop1)). // offset to first stop point Offset(zp.Sub(stop1)). // offset to first stop point
Rotate(zp, a). // rotate to align gradient Rotate(zp, a). // rotate to align gradient
Scale(zp, f32.Pt(1/l, 1/l)) // scale gradient to right size Scale(zp, f32.Pt(1/l, 1/l)) // scale gradient to right size
} }
// clipSpaceTransform returns the scale and offset that transforms the given // clipSpaceTransform returns the scale and offset that transforms the given
@@ -1518,37 +1521,38 @@ func decodeToOutlineQuads(qs *quadSplitter, tr f32.Affine2D, pathData []byte) {
} }
// create GPU vertices for transformed r, find the bounds and establish texture transform. // create GPU vertices for transformed r, find the bounds and establish texture transform.
func (d *drawOps) boundsForTransformedRect(r f32.Rectangle, tr f32.Affine2D) (aux []byte, bnd f32.Rectangle, ptr f32.Affine2D) { func (d *drawOps) boundsForTransformedRect(r image.Rectangle, tr f32.Affine2D) (aux []byte, bnd image.Rectangle, ptr f32.Affine2D) {
if isPureOffset(tr) { ptr = f32.AffineId()
// fast-path to allow blitting of pure rectangles if tr == f32.AffineId() {
_, _, ox, _, _, oy := tr.Elems() // fast-path to allow blitting of pure rectangles.
off := f32.Pt(ox, oy) bnd = r
bnd.Min = r.Min.Add(off)
bnd.Max = r.Max.Add(off)
return return
} }
// transform all corners, find new bounds // transform all corners, find new bounds
corners := [4]f32.Point{ corners := [4]f32.Point{
tr.Transform(r.Min), tr.Transform(f32.Pt(r.Max.X, r.Min.Y)), tr.Transform(f32.FPt(r.Min)), tr.Transform(f32.Pt(float32(r.Max.X), float32(r.Min.Y))),
tr.Transform(r.Max), tr.Transform(f32.Pt(r.Min.X, r.Max.Y)), tr.Transform(f32.FPt(r.Max)), tr.Transform(f32.Pt(float32(r.Min.X), float32(r.Max.Y))),
}
fBounds := f32.Rectangle{
Min: f32.Pt(math.MaxFloat32, math.MaxFloat32),
Max: f32.Pt(-math.MaxFloat32, -math.MaxFloat32),
} }
bnd.Min = f32.Pt(math.MaxFloat32, math.MaxFloat32)
bnd.Max = f32.Pt(-math.MaxFloat32, -math.MaxFloat32)
for _, c := range corners { for _, c := range corners {
if c.X < bnd.Min.X { if c.X < fBounds.Min.X {
bnd.Min.X = c.X fBounds.Min.X = c.X
} }
if c.Y < bnd.Min.Y { if c.Y < fBounds.Min.Y {
bnd.Min.Y = c.Y fBounds.Min.Y = c.Y
} }
if c.X > bnd.Max.X { if c.X > fBounds.Max.X {
bnd.Max.X = c.X fBounds.Max.X = c.X
} }
if c.Y > bnd.Max.Y { if c.Y > fBounds.Max.Y {
bnd.Max.Y = c.Y fBounds.Max.Y = c.Y
} }
} }
bnd = fBounds.Round()
// build the GPU vertices // build the GPU vertices
l := len(d.vertCache) l := len(d.vertCache)
@@ -1562,21 +1566,28 @@ func (d *drawOps) boundsForTransformedRect(r f32.Rectangle, tr f32.Affine2D) (au
// establish the transform mapping from bounds rectangle to transformed corners // establish the transform mapping from bounds rectangle to transformed corners
var P1, P2, P3 f32.Point var P1, P2, P3 f32.Point
P1.X = (corners[1].X - bnd.Min.X) / (bnd.Max.X - bnd.Min.X) P1.X = (corners[1].X - fBounds.Min.X) / (fBounds.Max.X - fBounds.Min.X)
P1.Y = (corners[1].Y - bnd.Min.Y) / (bnd.Max.Y - bnd.Min.Y) P1.Y = (corners[1].Y - fBounds.Min.Y) / (fBounds.Max.Y - fBounds.Min.Y)
P2.X = (corners[2].X - bnd.Min.X) / (bnd.Max.X - bnd.Min.X) P2.X = (corners[2].X - fBounds.Min.X) / (fBounds.Max.X - fBounds.Min.X)
P2.Y = (corners[2].Y - bnd.Min.Y) / (bnd.Max.Y - bnd.Min.Y) P2.Y = (corners[2].Y - fBounds.Min.Y) / (fBounds.Max.Y - fBounds.Min.Y)
P3.X = (corners[3].X - bnd.Min.X) / (bnd.Max.X - bnd.Min.X) P3.X = (corners[3].X - fBounds.Min.X) / (fBounds.Max.X - fBounds.Min.X)
P3.Y = (corners[3].Y - bnd.Min.Y) / (bnd.Max.Y - bnd.Min.Y) P3.Y = (corners[3].Y - fBounds.Min.Y) / (fBounds.Max.Y - fBounds.Min.Y)
sx, sy := P2.X-P3.X, P2.Y-P3.Y sx, sy := P2.X-P3.X, P2.Y-P3.Y
ptr = f32.NewAffine2D(sx, P2.X-P1.X, P1.X-sx, sy, P2.Y-P1.Y, P1.Y-sy).Invert() ptr = f32.NewAffine2D(sx, P2.X-P1.X, P1.X-sx, sy, P2.Y-P1.Y, P1.Y-sy).Invert()
return return aux, bnd, ptr
} }
func isPureOffset(t f32.Affine2D) bool { // transformOffset a transform into two parts, one which is pure integer offset
a, b, _, d, e, _ := t.Elems() // and the other representing the scaling, shearing and rotation and fractional
return a == 1 && b == 0 && d == 0 && e == 1 // offset.
func transformOffset(t f32.Affine2D) (f32.Affine2D, image.Point) {
sx, hx, ox, hy, sy, oy := t.Elems()
iox, fox := math.Modf(float64(ox))
ioy, foy := math.Modf(float64(oy))
ft := f32.NewAffine2D(sx, hx, float32(fox), hy, sy, float32(foy))
ip := image.Pt(int(iox), int(ioy))
return ft, ip
} }
func newShaders(ctx driver.Device, vsrc, fsrc shader.Sources) (vert driver.VertexShader, frag driver.FragmentShader, err error) { func newShaders(ctx driver.Device, vsrc, fsrc shader.Sources) (vert driver.VertexShader, frag driver.FragmentShader, err error) {
+5 -3
View File
@@ -21,8 +21,10 @@ import (
var dumpImages = flag.Bool("saveimages", false, "save test images") var dumpImages = flag.Bool("saveimages", false, "save test images")
var clearCol = color.NRGBA{A: 0xff, R: 0xde, G: 0xad, B: 0xbe} var (
var clearColExpect = f32color.NRGBAToRGBA(clearCol) clearCol = color.NRGBA{A: 0xff, R: 0xde, G: 0xad, B: 0xbe}
clearColExpect = f32color.NRGBAToRGBA(clearCol)
)
func TestFramebufferClear(t *testing.T) { func TestFramebufferClear(t *testing.T) {
b := newDriver(t) b := newDriver(t)
@@ -202,5 +204,5 @@ func saveImage(file string, img image.Image) error {
if err := png.Encode(&buf, img); err != nil { if err := png.Encode(&buf, img); err != nil {
return err return err
} }
return os.WriteFile(file, buf.Bytes(), 0666) return os.WriteFile(file, buf.Bytes(), 0o666)
} }
+3 -8
View File
@@ -152,9 +152,7 @@ func newDirect3D11Device(api driver.Direct3D11) (driver.Device, error) {
} }
func (b *Backend) BeginFrame(target driver.RenderTarget, clear bool, viewport image.Point) driver.Texture { func (b *Backend) BeginFrame(target driver.RenderTarget, clear bool, viewport image.Point) driver.Texture {
var ( var renderTarget *d3d11.RenderTargetView
renderTarget *d3d11.RenderTargetView
)
if target != nil { if target != nil {
switch t := target.(type) { switch t := target.(type) {
case driver.Direct3D11RenderTarget: case driver.Direct3D11RenderTarget:
@@ -229,10 +227,7 @@ func (b *Backend) NewTexture(format driver.TextureFormat, width, height int, min
// Flags required by ID3D11DeviceContext::GenerateMips. // Flags required by ID3D11DeviceContext::GenerateMips.
bindFlags |= d3d11.BIND_SHADER_RESOURCE | d3d11.BIND_RENDER_TARGET bindFlags |= d3d11.BIND_SHADER_RESOURCE | d3d11.BIND_RENDER_TARGET
miscFlags |= d3d11.RESOURCE_MISC_GENERATE_MIPS miscFlags |= d3d11.RESOURCE_MISC_GENERATE_MIPS
dim := width dim := max(height, width)
if height > dim {
dim = height
}
log2 := 32 - bits.LeadingZeros32(uint32(dim)) - 1 log2 := 32 - bits.LeadingZeros32(uint32(dim)) - 1
nmipmaps = log2 + 1 nmipmaps = log2 + 1
} }
@@ -802,7 +797,7 @@ func (t *Texture) ReadPixels(src image.Rectangle, pixels []byte, stride int) err
mapSize := dstPitch * h mapSize := dstPitch * h
data := sliceOf(resMap.PData, mapSize) data := sliceOf(resMap.PData, mapSize)
width := w * 4 width := w * 4
for r := 0; r < h; r++ { for r := range h {
pixels := pixels[r*srcPitch:] pixels := pixels[r*srcPitch:]
copy(pixels[:width], data[r*dstPitch:]) copy(pixels[:width], data[r*dstPitch:])
} }
+5 -3
View File
@@ -96,8 +96,10 @@ type BlendFactor uint8
type Topology uint8 type Topology uint8
type TextureFilter uint8 type (
type TextureFormat uint8 TextureFilter uint8
TextureFormat uint8
)
type BufferBinding uint8 type BufferBinding uint8
@@ -217,7 +219,7 @@ func flipImageY(stride, height int, pixels []byte) {
// Flip image in y-direction. OpenGL's origin is in the lower // Flip image in y-direction. OpenGL's origin is in the lower
// left corner. // left corner.
row := make([]uint8, stride) row := make([]uint8, stride)
for y := 0; y < height/2; y++ { for y := range height / 2 {
y1 := height - y - 1 y1 := height - y - 1
dest := y1 * stride dest := y1 * stride
src := y * stride src := y * stride
+5 -12
View File
@@ -8,6 +8,7 @@ import (
"image" "image"
"math/bits" "math/bits"
"runtime" "runtime"
"slices"
"strings" "strings"
"time" "time"
"unsafe" "unsafe"
@@ -717,10 +718,7 @@ func (b *Backend) NewTexture(format driver.TextureFormat, width, height int, min
if mipmap { if mipmap {
nmipmaps := 1 nmipmaps := 1
if mipmap { if mipmap {
dim := width dim := max(height, width)
if height > dim {
dim = height
}
log2 := 32 - bits.LeadingZeros32(uint32(dim)) - 1 log2 := 32 - bits.LeadingZeros32(uint32(dim)) - 1
nmipmaps = log2 + 1 nmipmaps = log2 + 1
} }
@@ -1132,7 +1130,7 @@ func (b *Backend) setupVertexArrays() {
enabled[inp.Location] = true enabled[inp.Location] = true
b.glstate.vertexAttribPointer(b.funcs, buf.obj, inp.Location, l.Size, gltyp, false, p.layout.Stride, buf.offset+l.Offset) b.glstate.vertexAttribPointer(b.funcs, buf.obj, inp.Location, l.Size, gltyp, false, p.layout.Stride, buf.offset+l.Offset)
} }
for i := 0; i < max; i++ { for i := range max {
b.glstate.setVertexAttribArray(b.funcs, i, enabled[i]) b.glstate.setVertexAttribArray(b.funcs, i, enabled[i])
} }
} }
@@ -1175,7 +1173,7 @@ func (t *texture) ReadPixels(src image.Rectangle, pixels []byte, stride int) err
} else { } else {
tmp := make([]byte, w*h*4) tmp := make([]byte, w*h*4)
t.backend.funcs.ReadPixels(src.Min.X, src.Min.Y, w, h, gl.RGBA, gl.UNSIGNED_BYTE, tmp) t.backend.funcs.ReadPixels(src.Min.X, src.Min.Y, w, h, gl.RGBA, gl.UNSIGNED_BYTE, tmp)
for y := 0; y < h; y++ { for y := range h {
copy(pixels[y*stride:], tmp[y*w*4:]) copy(pixels[y*stride:], tmp[y*w*4:])
} }
} }
@@ -1357,12 +1355,7 @@ func alphaTripleFor(ver [2]int) textureTriple {
} }
func hasExtension(exts []string, ext string) bool { func hasExtension(exts []string, ext string) bool {
for _, e := range exts { return slices.Contains(exts, ext)
if ext == e {
return true
}
}
return false
} }
func firstBufferType(typ driver.BufferBinding) gl.Enum { func firstBufferType(typ driver.BufferBinding) gl.Enum {
+21 -21
View File
@@ -66,8 +66,8 @@ func BenchmarkDrawUICached(b *testing.B) {
defer w.Release() defer w.Release()
drawCore(gtx, th) drawCore(gtx, th)
w.Frame(gtx.Ops) w.Frame(gtx.Ops)
b.ResetTimer()
for i := 0; i < b.N; i++ { for b.Loop() {
w.Frame(gtx.Ops) w.Frame(gtx.Ops)
} }
finishBenchmark(b, w) finishBenchmark(b, w)
@@ -83,12 +83,12 @@ func BenchmarkDrawUI(b *testing.B) {
drawCore(gtx, th) drawCore(gtx, th)
w.Frame(gtx.Ops) w.Frame(gtx.Ops)
b.ReportAllocs() b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ { for i := 0; b.Loop(); i++ {
resetOps(gtx) resetOps(gtx)
off := float32(math.Mod(float64(i)/10, 10)) off := float32(math.Mod(float64(i)/10, 10))
t := op.Affine(f32.Affine2D{}.Offset(f32.Pt(off, off))).Push(gtx.Ops) t := op.Affine(f32.AffineId().Offset(f32.Pt(off, off))).Push(gtx.Ops)
drawCore(gtx, th) drawCore(gtx, th)
@@ -105,12 +105,12 @@ func BenchmarkDrawUITransformed(b *testing.B) {
drawCore(gtx, th) drawCore(gtx, th)
w.Frame(gtx.Ops) w.Frame(gtx.Ops)
b.ReportAllocs() b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ { for i := 0; b.Loop(); i++ {
resetOps(gtx) resetOps(gtx)
angle := float32(math.Mod(float64(i)/1000, 0.05)) angle := float32(math.Mod(float64(i)/1000, 0.05))
a := f32.Affine2D{}.Shear(f32.Point{}, angle, angle).Rotate(f32.Point{}, angle) a := f32.AffineId().Shear(f32.Point{}, angle, angle).Rotate(f32.Point{}, angle)
t := op.Affine(a).Push(gtx.Ops) t := op.Affine(a).Push(gtx.Ops)
drawCore(gtx, th) drawCore(gtx, th)
@@ -130,8 +130,8 @@ func Benchmark1000Circles(b *testing.B) {
draw1000Circles(gtx) draw1000Circles(gtx)
w.Frame(gtx.Ops) w.Frame(gtx.Ops)
b.ReportAllocs() b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ { for b.Loop() {
resetOps(gtx) resetOps(gtx)
draw1000Circles(gtx) draw1000Circles(gtx)
w.Frame(gtx.Ops) w.Frame(gtx.Ops)
@@ -147,8 +147,8 @@ func Benchmark1000CirclesInstanced(b *testing.B) {
draw1000CirclesInstanced(gtx) draw1000CirclesInstanced(gtx)
w.Frame(gtx.Ops) w.Frame(gtx.Ops)
b.ReportAllocs() b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ { for b.Loop() {
resetOps(gtx) resetOps(gtx)
draw1000CirclesInstanced(gtx) draw1000CirclesInstanced(gtx)
w.Frame(gtx.Ops) w.Frame(gtx.Ops)
@@ -158,9 +158,9 @@ func Benchmark1000CirclesInstanced(b *testing.B) {
func draw1000Circles(gtx layout.Context) { func draw1000Circles(gtx layout.Context) {
ops := gtx.Ops ops := gtx.Ops
for x := 0; x < 100; x++ { for x := range 100 {
op.Offset(image.Pt(x*10, 0)).Add(ops) op.Offset(image.Pt(x*10, 0)).Add(ops)
for y := 0; y < 10; y++ { for y := range 10 {
paint.FillShape(ops, paint.FillShape(ops,
color.NRGBA{R: 100 + uint8(x), G: 100 + uint8(y), B: 100, A: 120}, color.NRGBA{R: 100 + uint8(x), G: 100 + uint8(y), B: 100, A: 120},
clip.RRect{Rect: image.Rect(0, 0, 10, 10), NE: 5, SE: 5, SW: 5, NW: 5}.Op(ops), clip.RRect{Rect: image.Rect(0, 0, 10, 10), NE: 5, SE: 5, SW: 5, NW: 5}.Op(ops),
@@ -179,9 +179,9 @@ func draw1000CirclesInstanced(gtx layout.Context) {
cl.Pop() cl.Pop()
c := r.Stop() c := r.Stop()
for x := 0; x < 100; x++ { for x := range 100 {
op.Offset(image.Pt(x*10, 0)).Add(ops) op.Offset(image.Pt(x*10, 0)).Add(ops)
for y := 0; y < 10; y++ { for y := range 10 {
paint.ColorOp{Color: color.NRGBA{R: 100 + uint8(x), G: 100 + uint8(y), B: 100, A: 120}}.Add(ops) paint.ColorOp{Color: color.NRGBA{R: 100 + uint8(x), G: 100 + uint8(y), B: 100, A: 120}}.Add(ops)
c.Add(ops) c.Add(ops)
op.Offset(image.Pt(0, 100)).Add(ops) op.Offset(image.Pt(0, 100)).Add(ops)
@@ -204,9 +204,9 @@ func drawIndividualShapes(gtx layout.Context, th *material.Theme) chan op.CallOp
go func() { go func() {
ops := &op1 ops := &op1
c := op.Record(ops) c := op.Record(ops)
for x := 0; x < 9; x++ { for x := range 9 {
op.Offset(image.Pt(x*50, 0)).Add(ops) op.Offset(image.Pt(x*50, 0)).Add(ops)
for y := 0; y < 9; y++ { for y := range 9 {
paint.FillShape(ops, paint.FillShape(ops,
color.NRGBA{R: 100 + uint8(x), G: 100 + uint8(y), B: 100, A: 120}, color.NRGBA{R: 100 + uint8(x), G: 100 + uint8(y), B: 100, A: 120},
clip.RRect{Rect: image.Rect(0, 0, 25, 25), NE: 10, SE: 10, SW: 10, NW: 10}.Op(ops), clip.RRect{Rect: image.Rect(0, 0, 25, 25), NE: 10, SE: 10, SW: 10, NW: 10}.Op(ops),
@@ -233,8 +233,8 @@ func drawShapeInstances(gtx layout.Context, th *material.Theme) chan op.CallOp {
squares.Add(ops) squares.Add(ops)
rad := float32(0) rad := float32(0)
for x := 0; x < 20; x++ { for x := range 20 {
for y := 0; y < 20; y++ { for y := range 20 {
t := op.Offset(image.Pt(x*50+25, y*50+25)).Push(ops) t := op.Offset(image.Pt(x*50+25, y*50+25)).Push(ops)
c.Add(ops) c.Add(ops)
t.Pop() t.Pop()
@@ -253,7 +253,7 @@ func drawText(gtx layout.Context, th *material.Theme) chan op.CallOp {
c := op.Record(ops) c := op.Record(ops)
txt := material.H6(th, "") txt := material.H6(th, "")
for x := 0; x < 40; x++ { for x := range 40 {
txt.Text = textRows[x] txt.Text = textRows[x]
t := op.Offset(image.Pt(0, 24*x)).Push(ops) t := op.Offset(image.Pt(0, 24*x)).Push(ops)
gtx.Ops = ops gtx.Ops = ops
+40 -9
View File
@@ -40,6 +40,25 @@ func TestPaintClippedRect(t *testing.T) {
}) })
} }
func TestPaintClippedRectOffset(t *testing.T) {
run(t, func(o *op.Ops) {
defer op.Affine(f32.AffineId().Offset(f32.Pt(0.5, 0.5))).Push(o).Pop()
defer clip.RRect{Rect: image.Rect(25, 25, 60, 60)}.Push(o).Pop()
paint.FillShape(o, red, clip.Rect(image.Rect(0, 0, 50, 50)).Op())
}, func(r result) {
r.expect(0, 0, transparent)
r.expect(24, 35, transparent)
r.expect(24, 24, transparent)
r.expect(25, 25, color.RGBA{R: 137, A: 64})
r.expect(25, 35, color.RGBA{R: 187, A: 128})
r.expect(35, 25, color.RGBA{R: 187, A: 128})
r.expect(50, 50, color.RGBA{R: 137, A: 64})
r.expect(51, 51, transparent)
r.expect(50, 0, transparent)
r.expect(10, 50, transparent)
})
}
func TestPaintClippedCircle(t *testing.T) { func TestPaintClippedCircle(t *testing.T) {
run(t, func(o *op.Ops) { run(t, func(o *op.Ops) {
const r = 10 const r = 10
@@ -132,8 +151,7 @@ func TestTexturedStrokeClipped(t *testing.T) {
defer clip.RRect{Rect: image.Rect(-30, -30, 60, 60)}.Push(o).Pop() defer clip.RRect{Rect: image.Rect(-30, -30, 60, 60)}.Push(o).Pop()
defer op.Offset(image.Pt(-10, -10)).Push(o).Pop() defer op.Offset(image.Pt(-10, -10)).Push(o).Pop()
paint.PaintOp{}.Add(o) paint.PaintOp{}.Add(o)
}, func(r result) { }, nil)
})
} }
func TestTexturedStroke(t *testing.T) { func TestTexturedStroke(t *testing.T) {
@@ -146,8 +164,7 @@ func TestTexturedStroke(t *testing.T) {
}.Op().Push(o).Pop() }.Op().Push(o).Pop()
defer op.Offset(image.Pt(-10, -10)).Push(o).Pop() defer op.Offset(image.Pt(-10, -10)).Push(o).Pop()
paint.PaintOp{}.Add(o) paint.PaintOp{}.Add(o)
}, func(r result) { }, nil)
})
} }
func TestPaintClippedTexture(t *testing.T) { func TestPaintClippedTexture(t *testing.T) {
@@ -178,7 +195,6 @@ func TestStrokedPathZeroWidth(t *testing.T) {
paint.Fill(o, red) paint.Fill(o, red)
cl.Pop() cl.Pop()
} }
}, func(r result) { }, func(r result) {
r.expect(0, 0, transparent) r.expect(0, 0, transparent)
r.expect(10, 50, colornames.Black) r.expect(10, 50, colornames.Black)
@@ -252,8 +268,7 @@ func TestPathReuse(t *testing.T) {
stroke := clip.Stroke{Path: spec, Width: 3}.Op().Push(o) stroke := clip.Stroke{Path: spec, Width: 3}.Op().Push(o)
paint.Fill(o, color.NRGBA{B: 0xFF, A: 0xFF}) paint.Fill(o, color.NRGBA{B: 0xFF, A: 0xFF})
stroke.Pop() stroke.Pop()
}, func(r result) { }, nil)
})
} }
func TestPathInterleave(t *testing.T) { func TestPathInterleave(t *testing.T) {
@@ -290,6 +305,22 @@ func TestStrokedRect(t *testing.T) {
Width: 5, Width: 5,
}.Op(), }.Op(),
) )
}, func(r result) { }, nil)
}) }
func TestInstancedRects(t *testing.T) {
run(t, func(o *op.Ops) {
macro := op.Record(o)
clip := clip.Rect{Max: image.Pt(20, 20)}.Push(o)
paint.ColorOp{Color: color.NRGBA{R: 0x80, A: 0xFF}}.Add(o)
paint.PaintOp{}.Add(o)
clip.Pop()
c := macro.Stop()
for range 2 {
op.Affine(f32.AffineId().Rotate(f32.Pt(0, 0), .2)).Add(o)
c.Add(o)
op.Offset(image.Pt(20, 20)).Add(o)
}
}, nil)
} }
Binary file not shown.

After

Width:  |  Height:  |  Size: 787 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 435 B

+14 -15
View File
@@ -24,7 +24,6 @@ func TestTransformMacro(t *testing.T) {
c := constSqPath() c := constSqPath()
run(t, func(o *op.Ops) { run(t, func(o *op.Ops) {
// render the first Stacked item // render the first Stacked item
m1 := op.Record(o) m1 := op.Record(o)
dr := image.Rect(0, 0, 128, 50) dr := image.Rect(0, 0, 128, 50)
@@ -88,10 +87,10 @@ func TestNoClipFromPaint(t *testing.T) {
// ensure that a paint operation does not pollute the state // ensure that a paint operation does not pollute the state
// by leaving any clip paths in place. // by leaving any clip paths in place.
run(t, func(o *op.Ops) { run(t, func(o *op.Ops) {
a := f32.Affine2D{}.Rotate(f32.Pt(20, 20), math.Pi/4) a := f32.AffineId().Rotate(f32.Pt(20, 20), math.Pi/4)
defer op.Affine(a).Push(o).Pop() defer op.Affine(a).Push(o).Pop()
paint.FillShape(o, red, clip.Rect(image.Rect(10, 10, 30, 30)).Op()) paint.FillShape(o, red, clip.Rect(image.Rect(10, 10, 30, 30)).Op())
a = f32.Affine2D{}.Rotate(f32.Pt(20, 20), -math.Pi/4) a = f32.AffineId().Rotate(f32.Pt(20, 20), -math.Pi/4)
defer op.Affine(a).Push(o).Pop() defer op.Affine(a).Push(o).Pop()
paint.FillShape(o, black, clip.Rect(image.Rect(0, 0, 50, 50)).Op()) paint.FillShape(o, black, clip.Rect(image.Rect(0, 0, 50, 50)).Op())
@@ -110,7 +109,7 @@ func TestDeferredPaint(t *testing.T) {
paint.PaintOp{}.Add(o) paint.PaintOp{}.Add(o)
cl.Pop() cl.Pop()
t := op.Affine(f32.Affine2D{}.Offset(f32.Pt(20, 20))).Push(o) t := op.Affine(f32.AffineId().Offset(f32.Pt(20, 20))).Push(o)
m := op.Record(o) m := op.Record(o)
cl2 := clip.Rect(image.Rect(0, 0, 80, 80)).Op().Push(o) cl2 := clip.Rect(image.Rect(0, 0, 80, 80)).Op().Push(o)
paint.ColorOp{Color: color.NRGBA{A: 0x60, R: 0xff, G: 0xff}}.Add(o) paint.ColorOp{Color: color.NRGBA{A: 0x60, R: 0xff, G: 0xff}}.Add(o)
@@ -120,12 +119,11 @@ func TestDeferredPaint(t *testing.T) {
op.Defer(o, paintMacro) op.Defer(o, paintMacro)
t.Pop() t.Pop()
defer op.Affine(f32.Affine2D{}.Offset(f32.Pt(10, 10))).Push(o).Pop() defer op.Affine(f32.AffineId().Offset(f32.Pt(10, 10))).Push(o).Pop()
defer clip.Rect(image.Rect(0, 0, 80, 80)).Op().Push(o).Pop() defer clip.Rect(image.Rect(0, 0, 80, 80)).Op().Push(o).Pop()
paint.ColorOp{Color: color.NRGBA{A: 0x60, B: 0xff}}.Add(o) paint.ColorOp{Color: color.NRGBA{A: 0x60, B: 0xff}}.Add(o)
paint.PaintOp{}.Add(o) paint.PaintOp{}.Add(o)
}, func(r result) { }, nil)
})
} }
func constSqPath() clip.Op { func constSqPath() clip.Op {
@@ -143,8 +141,10 @@ func constSqPath() clip.Op {
func constSqCirc() clip.Op { func constSqCirc() clip.Op {
innerOps := new(op.Ops) innerOps := new(op.Ops)
return clip.RRect{Rect: image.Rect(0, 0, 40, 40), return clip.RRect{
NW: 20, NE: 20, SW: 20, SE: 20}.Op(innerOps) Rect: image.Rect(0, 0, 40, 40),
NW: 20, NE: 20, SW: 20, SE: 20,
}.Op(innerOps)
} }
func drawChild(ops *op.Ops, text clip.Op) op.CallOp { func drawChild(ops *op.Ops, text clip.Op) op.CallOp {
@@ -260,7 +260,7 @@ func TestLinearGradient(t *testing.T) {
Color2: g.To, Color2: g.To,
}.Add(ops) }.Add(ops)
cl := clip.RRect{Rect: gr.Round()}.Push(ops) cl := clip.RRect{Rect: gr.Round()}.Push(ops)
t1 := op.Affine(f32.Affine2D{}.Offset(pixelAligned.Min)).Push(ops) t1 := op.Affine(f32.AffineId().Offset(pixelAligned.Min)).Push(ops)
t2 := scale(pixelAligned.Dx()/128, 1).Push(ops) t2 := scale(pixelAligned.Dx()/128, 1).Push(ops)
paint.PaintOp{}.Add(ops) paint.PaintOp{}.Add(ops)
t2.Pop() t2.Pop()
@@ -323,7 +323,7 @@ func TestLinearGradientAngled(t *testing.T) {
cl = clip.Rect(image.Rect(0, 64, 64, 128)).Push(ops) cl = clip.Rect(image.Rect(0, 64, 64, 128)).Push(ops)
paint.PaintOp{}.Add(ops) paint.PaintOp{}.Add(ops)
cl.Pop() cl.Pop()
}, func(r result) {}) }, nil)
} }
func TestZeroImage(t *testing.T) { func TestZeroImage(t *testing.T) {
@@ -363,7 +363,7 @@ func TestImageRGBA_ScaleLinear(t *testing.T) {
run(t, func(o *op.Ops) { run(t, func(o *op.Ops) {
w := newWindow(t, 128, 128) w := newWindow(t, 128, 128)
defer clip.Rect{Max: image.Pt(128, 128)}.Push(o).Pop() defer clip.Rect{Max: image.Pt(128, 128)}.Push(o).Pop()
op.Affine(f32.Affine2D{}.Scale(f32.Point{}, f32.Pt(64, 64))).Add(o) op.Affine(f32.AffineId().Scale(f32.Point{}, f32.Pt(64, 64))).Add(o)
im := image.NewRGBA(image.Rect(0, 0, 2, 2)) im := image.NewRGBA(image.Rect(0, 0, 2, 2))
im.Set(0, 0, colornames.Red) im.Set(0, 0, colornames.Red)
@@ -397,7 +397,7 @@ func TestImageRGBA_ScaleLinear(t *testing.T) {
func TestImageRGBA_ScaleNearest(t *testing.T) { func TestImageRGBA_ScaleNearest(t *testing.T) {
run(t, func(o *op.Ops) { run(t, func(o *op.Ops) {
w := newWindow(t, 128, 128) w := newWindow(t, 128, 128)
op.Affine(f32.Affine2D{}.Scale(f32.Point{}, f32.Pt(64, 64))).Add(o) op.Affine(f32.AffineId().Scale(f32.Point{}, f32.Pt(64, 64))).Add(o)
im := image.NewRGBA(image.Rect(0, 0, 2, 2)) im := image.NewRGBA(image.Rect(0, 0, 2, 2))
im.Set(0, 0, colornames.Red) im.Set(0, 0, colornames.Red)
@@ -492,8 +492,7 @@ func TestOpacity(t *testing.T) {
opc3 := paint.PushOpacity(ops, .6) opc3 := paint.PushOpacity(ops, .6)
paint.FillShape(ops, color.NRGBA{B: 255, A: 255}, clip.Ellipse(image.Rectangle{Min: image.Pt(20+20, 10), Max: image.Pt(50+64, 128)}).Op(ops)) paint.FillShape(ops, color.NRGBA{B: 255, A: 255}, clip.Ellipse(image.Rectangle{Min: image.Pt(20+20, 10), Max: image.Pt(50+64, 128)}).Op(ops))
opc3.Pop() opc3.Pop()
}, func(r result) { }, nil)
})
} }
// lerp calculates linear interpolation with color b and p. // lerp calculates linear interpolation with color b and p.
+12 -12
View File
@@ -29,7 +29,7 @@ func TestPaintOffset(t *testing.T) {
func TestPaintRotate(t *testing.T) { func TestPaintRotate(t *testing.T) {
run(t, func(o *op.Ops) { run(t, func(o *op.Ops) {
a := f32.Affine2D{}.Rotate(f32.Pt(40, 40), -math.Pi/8) a := f32.AffineId().Rotate(f32.Pt(40, 40), -math.Pi/8)
defer op.Affine(a).Push(o).Pop() defer op.Affine(a).Push(o).Pop()
paint.FillShape(o, red, clip.Rect(image.Rect(20, 20, 60, 60)).Op()) paint.FillShape(o, red, clip.Rect(image.Rect(20, 20, 60, 60)).Op())
}, func(r result) { }, func(r result) {
@@ -42,7 +42,7 @@ func TestPaintRotate(t *testing.T) {
func TestPaintShear(t *testing.T) { func TestPaintShear(t *testing.T) {
run(t, func(o *op.Ops) { run(t, func(o *op.Ops) {
a := f32.Affine2D{}.Shear(f32.Point{}, math.Pi/4, 0) a := f32.AffineId().Shear(f32.Point{}, math.Pi/4, 0)
defer op.Affine(a).Push(o).Pop() defer op.Affine(a).Push(o).Pop()
paint.FillShape(o, red, clip.Rect(image.Rect(0, 0, 40, 40)).Op()) paint.FillShape(o, red, clip.Rect(image.Rect(0, 0, 40, 40)).Op())
}, func(r result) { }, func(r result) {
@@ -79,7 +79,7 @@ func TestClipOffset(t *testing.T) {
func TestClipScale(t *testing.T) { func TestClipScale(t *testing.T) {
run(t, func(o *op.Ops) { run(t, func(o *op.Ops) {
a := f32.Affine2D{}.Scale(f32.Point{}, f32.Pt(2, 2)).Offset(f32.Pt(10, 10)) a := f32.AffineId().Scale(f32.Point{}, f32.Pt(2, 2)).Offset(f32.Pt(10, 10))
defer op.Affine(a).Push(o).Pop() defer op.Affine(a).Push(o).Pop()
defer clip.RRect{Rect: image.Rect(10, 10, 20, 20)}.Push(o).Pop() defer clip.RRect{Rect: image.Rect(10, 10, 20, 20)}.Push(o).Pop()
paint.FillShape(o, red, clip.Rect(image.Rect(0, 0, 1000, 1000)).Op()) paint.FillShape(o, red, clip.Rect(image.Rect(0, 0, 1000, 1000)).Op())
@@ -93,7 +93,7 @@ func TestClipScale(t *testing.T) {
func TestClipRotate(t *testing.T) { func TestClipRotate(t *testing.T) {
run(t, func(o *op.Ops) { run(t, func(o *op.Ops) {
defer op.Affine(f32.Affine2D{}.Rotate(f32.Pt(40, 40), -math.Pi/4)).Push(o).Pop() defer op.Affine(f32.AffineId().Rotate(f32.Pt(40, 40), -math.Pi/4)).Push(o).Pop()
defer clip.RRect{Rect: image.Rect(30, 30, 50, 50)}.Push(o).Pop() defer clip.RRect{Rect: image.Rect(30, 30, 50, 50)}.Push(o).Pop()
paint.FillShape(o, red, clip.Rect(image.Rect(0, 40, 100, 100)).Op()) paint.FillShape(o, red, clip.Rect(image.Rect(0, 40, 100, 100)).Op())
}, func(r result) { }, func(r result) {
@@ -121,7 +121,7 @@ func TestOffsetScaleTexture(t *testing.T) {
run(t, func(o *op.Ops) { run(t, func(o *op.Ops) {
defer op.Offset(image.Pt(15, 15)).Push(o).Pop() defer op.Offset(image.Pt(15, 15)).Push(o).Pop()
squares.Add(o) squares.Add(o)
defer op.Affine(f32.Affine2D{}.Scale(f32.Point{}, f32.Pt(2, 1))).Push(o).Pop() defer op.Affine(f32.AffineId().Scale(f32.Point{}, f32.Pt(2, 1))).Push(o).Pop()
defer scale(50.0/512, 50.0/512).Push(o).Pop() defer scale(50.0/512, 50.0/512).Push(o).Pop()
paint.PaintOp{}.Add(o) paint.PaintOp{}.Add(o)
}, func(r result) { }, func(r result) {
@@ -133,7 +133,7 @@ func TestOffsetScaleTexture(t *testing.T) {
func TestRotateTexture(t *testing.T) { func TestRotateTexture(t *testing.T) {
run(t, func(o *op.Ops) { run(t, func(o *op.Ops) {
squares.Add(o) squares.Add(o)
a := f32.Affine2D{}.Offset(f32.Pt(30, 30)).Rotate(f32.Pt(40, 40), math.Pi/4) a := f32.AffineId().Offset(f32.Pt(30, 30)).Rotate(f32.Pt(40, 40), math.Pi/4)
defer op.Affine(a).Push(o).Pop() defer op.Affine(a).Push(o).Pop()
defer scale(20.0/512, 20.0/512).Push(o).Pop() defer scale(20.0/512, 20.0/512).Push(o).Pop()
paint.PaintOp{}.Add(o) paint.PaintOp{}.Add(o)
@@ -146,10 +146,10 @@ func TestRotateTexture(t *testing.T) {
func TestRotateClipTexture(t *testing.T) { func TestRotateClipTexture(t *testing.T) {
run(t, func(o *op.Ops) { run(t, func(o *op.Ops) {
squares.Add(o) squares.Add(o)
a := f32.Affine2D{}.Rotate(f32.Pt(40, 40), math.Pi/8) a := f32.AffineId().Rotate(f32.Pt(40, 40), math.Pi/8)
defer op.Affine(a).Push(o).Pop() defer op.Affine(a).Push(o).Pop()
defer clip.RRect{Rect: image.Rect(30, 30, 50, 50)}.Push(o).Pop() defer clip.RRect{Rect: image.Rect(30, 30, 50, 50)}.Push(o).Pop()
defer op.Affine(f32.Affine2D{}.Offset(f32.Pt(10, 10))).Push(o).Pop() defer op.Affine(f32.AffineId().Offset(f32.Pt(10, 10))).Push(o).Pop()
defer scale(60.0/512, 60.0/512).Push(o).Pop() defer scale(60.0/512, 60.0/512).Push(o).Pop()
paint.PaintOp{}.Add(o) paint.PaintOp{}.Add(o)
}, func(r result) { }, func(r result) {
@@ -168,7 +168,7 @@ func TestComplicatedTransform(t *testing.T) {
defer clip.RRect{Rect: image.Rect(0, 0, 100, 100), SE: 50, SW: 50, NW: 50, NE: 50}.Push(o).Pop() defer clip.RRect{Rect: image.Rect(0, 0, 100, 100), SE: 50, SW: 50, NW: 50, NE: 50}.Push(o).Pop()
a := f32.Affine2D{}.Shear(f32.Point{}, math.Pi/4, 0) a := f32.AffineId().Shear(f32.Point{}, math.Pi/4, 0)
defer op.Affine(a).Push(o).Pop() defer op.Affine(a).Push(o).Pop()
defer clip.RRect{Rect: image.Rect(0, 0, 50, 40)}.Push(o).Pop() defer clip.RRect{Rect: image.Rect(0, 0, 50, 40)}.Push(o).Pop()
@@ -182,13 +182,13 @@ func TestComplicatedTransform(t *testing.T) {
func TestTransformOrder(t *testing.T) { func TestTransformOrder(t *testing.T) {
// check the ordering of operations bot in affine and in gpu stack. // check the ordering of operations bot in affine and in gpu stack.
run(t, func(o *op.Ops) { run(t, func(o *op.Ops) {
a := f32.Affine2D{}.Offset(f32.Pt(64, 64)) a := f32.AffineId().Offset(f32.Pt(64, 64))
defer op.Affine(a).Push(o).Pop() defer op.Affine(a).Push(o).Pop()
b := f32.Affine2D{}.Scale(f32.Point{}, f32.Pt(8, 8)) b := f32.AffineId().Scale(f32.Point{}, f32.Pt(8, 8))
defer op.Affine(b).Push(o).Pop() defer op.Affine(b).Push(o).Pop()
c := f32.Affine2D{}.Offset(f32.Pt(-10, -10)).Scale(f32.Point{}, f32.Pt(0.5, 0.5)) c := f32.AffineId().Offset(f32.Pt(-10, -10)).Scale(f32.Point{}, f32.Pt(0.5, 0.5))
defer op.Affine(c).Push(o).Pop() defer op.Affine(c).Push(o).Pop()
paint.FillShape(o, red, clip.Rect(image.Rect(0, 0, 20, 20)).Op()) paint.FillShape(o, red, clip.Rect(image.Rect(0, 0, 20, 20)).Op())
}, func(r result) { }, func(r result) {
+9 -7
View File
@@ -49,8 +49,8 @@ func buildSquares(size int) paint.ImageOp {
sub := size / 4 sub := size / 4
im := image.NewNRGBA(image.Rect(0, 0, size, size)) im := image.NewNRGBA(image.Rect(0, 0, size, size))
c1, c2 := image.NewUniform(colornames.Green), image.NewUniform(colornames.Blue) c1, c2 := image.NewUniform(colornames.Green), image.NewUniform(colornames.Blue)
for r := 0; r < 4; r++ { for r := range 4 {
for c := 0; c < 4; c++ { for c := range 4 {
c1, c2 = c2, c1 c1, c2 = c2, c1
draw.Draw(im, image.Rect(r*sub, c*sub, r*sub+sub, c*sub+sub), c1, image.Point{}, draw.Over) draw.Draw(im, image.Rect(r*sub, c*sub, r*sub+sub, c*sub+sub), c1, image.Point{}, draw.Over)
} }
@@ -78,7 +78,7 @@ func run(t *testing.T, f func(o *op.Ops), c func(r result)) {
var img *image.RGBA var img *image.RGBA
var err error var err error
ops := new(op.Ops) ops := new(op.Ops)
for i := 0; i < 3; i++ { for i := range 3 {
ops.Reset() ops.Reset()
img, err = drawImage(t, 128, ops, f) img, err = drawImage(t, 128, ops, f)
if err != nil { if err != nil {
@@ -90,7 +90,9 @@ func run(t *testing.T, f func(o *op.Ops), c func(r result)) {
name := fmt.Sprintf("%s-%d-bad.png", t.Name(), i) name := fmt.Sprintf("%s-%d-bad.png", t.Name(), i)
saveImage(t, name, img) saveImage(t, name, img)
} }
c(result{t: t, img: img}) if c != nil {
c(result{t: t, img: img})
}
} }
} }
@@ -151,7 +153,7 @@ func verifyRef(t *testing.T, img *image.RGBA, frame int) (ok bool) {
} }
path = filepath.Join("refs", path+".png") path = filepath.Join("refs", path+".png")
if *dumpImages { if *dumpImages {
if err := os.MkdirAll(filepath.Dir(path), 0766); err != nil { if err := os.MkdirAll(filepath.Dir(path), 0o766); err != nil {
if !os.IsExist(err) { if !os.IsExist(err) {
t.Error(err) t.Error(err)
return return
@@ -285,7 +287,7 @@ func saveImage(t testing.TB, file string, img *image.RGBA) {
t.Error(err) t.Error(err)
return return
} }
if err := os.WriteFile(file, buf.Bytes(), 0666); err != nil { if err := os.WriteFile(file, buf.Bytes(), 0o666); err != nil {
t.Error(err) t.Error(err)
return return
} }
@@ -300,5 +302,5 @@ func newWindow(t testing.TB, width, height int) *headless.Window {
} }
func scale(sx, sy float32) op.TransformOp { func scale(sx, sy float32) op.TransformOp {
return op.Affine(f32.Affine2D{}.Scale(f32.Point{}, f32.Pt(sx, sy))) return op.Affine(f32.AffineId().Scale(f32.Point{}, f32.Pt(sx, sy)))
} }
+2 -2
View File
@@ -10,10 +10,10 @@ import (
func BenchmarkPacker(b *testing.B) { func BenchmarkPacker(b *testing.B) {
var p packer var p packer
p.maxDims = image.Point{X: 4096, Y: 4096} p.maxDims = image.Point{X: 4096, Y: 4096}
for i := 0; i < b.N; i++ { for i := 0; b.Loop(); i++ {
p.clear() p.clear()
p.newPage() p.newPage()
for k := 0; k < 500; k++ { for k := range 500 {
_, ok := p.tryAdd(xy(k)) _, ok := p.tryAdd(xy(k))
if !ok { if !ok {
b.Fatal("add failed", i, k, xy(k)) b.Fatal("add failed", i, k, xy(k))
+5 -5
View File
@@ -150,7 +150,7 @@ func newCoverer(ctx driver.Device) *coverer {
c.texUniforms = new(coverTexUniforms) c.texUniforms = new(coverTexUniforms)
c.linearGradientUniforms = new(coverLinearGradientUniforms) c.linearGradientUniforms = new(coverLinearGradientUniforms)
pipelines, err := createColorPrograms(ctx, gio.Shader_cover_vert, gio.Shader_cover_frag, pipelines, err := createColorPrograms(ctx, gio.Shader_cover_vert, gio.Shader_cover_frag,
[3]interface{}{c.colUniforms, c.linearGradientUniforms, c.texUniforms}, [3]any{c.colUniforms, c.linearGradientUniforms, c.texUniforms},
) )
if err != nil { if err != nil {
panic(err) panic(err)
@@ -162,7 +162,7 @@ func newCoverer(ctx driver.Device) *coverer {
func newStenciler(ctx driver.Device) *stenciler { func newStenciler(ctx driver.Device) *stenciler {
// Allocate a suitably large index buffer for drawing paths. // Allocate a suitably large index buffer for drawing paths.
indices := make([]uint16, pathBatchSize*6) indices := make([]uint16, pathBatchSize*6)
for i := 0; i < pathBatchSize; i++ { for i := range pathBatchSize {
i := uint16(i) i := uint16(i)
indices[i*6+0] = i*4 + 0 indices[i*6+0] = i*4 + 0
indices[i*6+1] = i*4 + 1 indices[i*6+1] = i*4 + 1
@@ -334,7 +334,7 @@ func (p *pather) begin(sizes []image.Point) {
p.stenciler.begin(sizes) p.stenciler.begin(sizes)
} }
func (p *pather) stencilPath(bounds image.Rectangle, offset f32.Point, uv image.Point, data pathData) { func (p *pather) stencilPath(bounds image.Rectangle, offset image.Point, uv image.Point, data pathData) {
p.stenciler.stencilPath(bounds, offset, uv, data) p.stenciler.stencilPath(bounds, offset, uv, data)
} }
@@ -353,14 +353,14 @@ func (s *stenciler) begin(sizes []image.Point) {
s.fbos.resize(s.ctx, driver.TextureFormatFloat, sizes) s.fbos.resize(s.ctx, driver.TextureFormatFloat, sizes)
} }
func (s *stenciler) stencilPath(bounds image.Rectangle, offset f32.Point, uv image.Point, data pathData) { func (s *stenciler) stencilPath(bounds image.Rectangle, offset image.Point, uv image.Point, data pathData) {
s.ctx.Viewport(uv.X, uv.Y, bounds.Dx(), bounds.Dy()) s.ctx.Viewport(uv.X, uv.Y, bounds.Dx(), bounds.Dy())
// Transform UI coordinates to OpenGL coordinates. // Transform UI coordinates to OpenGL coordinates.
texSize := f32.Point{X: float32(bounds.Dx()), Y: float32(bounds.Dy())} texSize := f32.Point{X: float32(bounds.Dx()), Y: float32(bounds.Dy())}
scale := f32.Point{X: 2 / texSize.X, Y: 2 / texSize.Y} scale := f32.Point{X: 2 / texSize.X, Y: 2 / texSize.Y}
orig := f32.Point{X: -1 - float32(bounds.Min.X)*2/texSize.X, Y: -1 - float32(bounds.Min.Y)*2/texSize.Y} orig := f32.Point{X: -1 - float32(bounds.Min.X)*2/texSize.X, Y: -1 - float32(bounds.Min.Y)*2/texSize.Y}
s.pipeline.uniforms.transform = [4]float32{scale.X, scale.Y, orig.X, orig.Y} s.pipeline.uniforms.transform = [4]float32{scale.X, scale.Y, orig.X, orig.Y}
s.pipeline.uniforms.pathOffset = [2]float32{offset.X, offset.Y} s.pipeline.uniforms.pathOffset = [2]float32{float32(offset.X), float32(offset.Y)}
s.pipeline.pipeline.UploadUniforms(s.ctx) s.pipeline.pipeline.UploadUniforms(s.ctx)
// Draw in batches that fit in uint16 indices. // Draw in batches that fit in uint16 indices.
start := 0 start := 0
+2 -2
View File
@@ -10,7 +10,7 @@ import (
) )
// Struct returns a byte slice view of a struct. // Struct returns a byte slice view of a struct.
func Struct(s interface{}) []byte { func Struct(s any) []byte {
v := reflect.ValueOf(s) v := reflect.ValueOf(s)
sz := int(v.Elem().Type().Size()) sz := int(v.Elem().Type().Size())
return unsafe.Slice((*byte)(unsafe.Pointer(v.Pointer())), sz) return unsafe.Slice((*byte)(unsafe.Pointer(v.Pointer())), sz)
@@ -27,7 +27,7 @@ func Uint32(s []uint32) []byte {
} }
// Slice returns a byte slice view of a slice. // Slice returns a byte slice view of a slice.
func Slice(s interface{}) []byte { func Slice(s any) []byte {
v := reflect.ValueOf(s) v := reflect.ValueOf(s)
first := v.Index(0) first := v.Index(0)
sz := int(first.Type().Size()) sz := int(first.Type().Size())
+2 -6
View File
@@ -9,6 +9,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"runtime" "runtime"
"slices"
"strings" "strings"
"gioui.org/gpu" "gioui.org/gpu"
@@ -154,12 +155,7 @@ func (c *Context) EnableVSync(enable bool) {
} }
func hasExtension(exts []string, ext string) bool { func hasExtension(exts []string, ext string) bool {
for _, e := range exts { return slices.Contains(exts, ext)
if ext == e {
return true
}
}
return false
} }
func createContext(disp _EGLDisplay) (*eglContext, error) { func createContext(disp _EGLDisplay) (*eglContext, error) {
+1 -1
View File
@@ -186,6 +186,6 @@ func eglWaitClient() bool {
// issue34474KeepAlive calls runtime.KeepAlive as a // issue34474KeepAlive calls runtime.KeepAlive as a
// workaround for golang.org/issue/34474. // workaround for golang.org/issue/34474.
func issue34474KeepAlive(v interface{}) { func issue34474KeepAlive(v any) {
runtime.KeepAlive(v) runtime.KeepAlive(v)
} }
+2
View File
@@ -19,6 +19,8 @@ type Affine2D = f32.Affine2D
var NewAffine2D = f32.NewAffine2D var NewAffine2D = f32.NewAffine2D
var AffineId = f32.AffineId
// A Rectangle contains the points (X, Y) where Min.X <= X < Max.X, // A Rectangle contains the points (X, Y) where Min.X <= X < Max.X,
// Min.Y <= Y < Max.Y. // Min.Y <= Y < Max.Y.
type Rectangle struct { type Rectangle struct {
+2 -2
View File
@@ -16,7 +16,7 @@ func main() {
flag.Parse() flag.Parse()
var b bytes.Buffer var b bytes.Buffer
printf := func(content string, args ...interface{}) { printf := func(content string, args ...any) {
fmt.Fprintf(&b, content, args...) fmt.Fprintf(&b, content, args...)
} }
@@ -42,7 +42,7 @@ func main() {
panic(err) panic(err)
} }
err = os.WriteFile(*out, data, 0755) err = os.WriteFile(*out, data, 0o755)
if err != nil { if err != nil {
panic(err) panic(err)
} }
+3 -3
View File
@@ -41,17 +41,17 @@ var sink RGBA
func BenchmarkLinearFromSRGB(b *testing.B) { func BenchmarkLinearFromSRGB(b *testing.B) {
b.Run("opaque", func(b *testing.B) { b.Run("opaque", func(b *testing.B) {
for i := 0; i < b.N; i++ { for i := 0; b.Loop(); i++ {
sink = LinearFromSRGB(color.NRGBA{R: byte(i), G: byte(i >> 8), B: byte(i >> 16), A: 0xFF}) sink = LinearFromSRGB(color.NRGBA{R: byte(i), G: byte(i >> 8), B: byte(i >> 16), A: 0xFF})
} }
}) })
b.Run("translucent", func(b *testing.B) { b.Run("translucent", func(b *testing.B) {
for i := 0; i < b.N; i++ { for i := 0; b.Loop(); i++ {
sink = LinearFromSRGB(color.NRGBA{R: byte(i), G: byte(i >> 8), B: byte(i >> 16), A: 0x50}) sink = LinearFromSRGB(color.NRGBA{R: byte(i), G: byte(i >> 8), B: byte(i >> 16), A: 0x50})
} }
}) })
b.Run("transparent", func(b *testing.B) { b.Run("transparent", func(b *testing.B) {
for i := 0; i < b.N; i++ { for i := 0; b.Loop(); i++ {
sink = LinearFromSRGB(color.NRGBA{R: byte(i), G: byte(i >> 8), B: byte(i >> 16), A: 0x00}) sink = LinearFromSRGB(color.NRGBA{R: byte(i), G: byte(i >> 8), B: byte(i >> 16), A: 0x00})
} }
}) })
+15 -15
View File
@@ -90,7 +90,7 @@ func (e *Extrapolation) Estimate() Estimate {
first := e.get(0) first := e.get(0)
t := first.t t := first.t
// Walk backwards collecting samples. // Walk backwards collecting samples.
for i := 0; i < len(e.samples); i++ { for i := range e.samples {
p := e.get(-i) p := e.get(-i)
age := first.t - p.t age := first.t - p.t
if age >= maxAge || t-p.t >= maxSampleGap { if age >= maxAge || t-p.t >= maxSampleGap {
@@ -172,9 +172,9 @@ func decomposeQR(A *matrix) (*matrix, *matrix, bool) {
// https://en.wikipedia.org/wiki/Gram%E2%80%93Schmidt_process // https://en.wikipedia.org/wiki/Gram%E2%80%93Schmidt_process
Q := newMatrix(A.rows, A.cols) // Column-major. Q := newMatrix(A.rows, A.cols) // Column-major.
Rt := newMatrix(A.rows, A.rows) // R transposed, row-major. Rt := newMatrix(A.rows, A.rows) // R transposed, row-major.
for i := 0; i < Q.rows; i++ { for i := range Q.rows {
// Copy A column. // Copy A column.
for j := 0; j < Q.cols; j++ { for j := range Q.cols {
Q.set(i, j, A.get(i, j)) Q.set(i, j, A.get(i, j))
} }
// Subtract projections. Note that int the projection // Subtract projections. Note that int the projection
@@ -184,9 +184,9 @@ func decomposeQR(A *matrix) (*matrix, *matrix, bool) {
// the normalized column e replaces u, where <e, e> = 1: // the normalized column e replaces u, where <e, e> = 1:
// //
// proje a = <e, a>/<e, e> e = <e, a> e // proje a = <e, a>/<e, e> e = <e, a> e
for j := 0; j < i; j++ { for j := range i {
d := dot(Q.col(j), Q.col(i)) d := dot(Q.col(j), Q.col(i))
for k := 0; k < Q.cols; k++ { for k := range Q.cols {
Q.set(i, k, Q.get(i, k)-d*Q.get(j, k)) Q.set(i, k, Q.get(i, k)-d*Q.get(j, k))
} }
} }
@@ -197,7 +197,7 @@ func decomposeQR(A *matrix) (*matrix, *matrix, bool) {
return nil, nil, false return nil, nil, false
} }
invNorm := 1 / n invNorm := 1 / n
for j := 0; j < Q.cols; j++ { for j := range Q.cols {
Q.set(i, j, Q.get(i, j)*invNorm) Q.set(i, j, Q.get(i, j)*invNorm)
} }
// Update Rt. // Update Rt.
@@ -261,8 +261,8 @@ func (m *matrix) approxEqual(m2 *matrix) bool {
return false return false
} }
const epsilon = 0.00001 const epsilon = 0.00001
for row := 0; row < m.rows; row++ { for row := range m.rows {
for col := 0; col < m.cols; col++ { for col := range m.cols {
d := m2.get(row, col) - m.get(row, col) d := m2.get(row, col) - m.get(row, col)
if d < -epsilon || d > epsilon { if d < -epsilon || d > epsilon {
return false return false
@@ -278,8 +278,8 @@ func (m *matrix) transpose() *matrix {
cols: m.rows, cols: m.rows,
data: make([]float32, len(m.data)), data: make([]float32, len(m.data)),
} }
for i := 0; i < m.rows; i++ { for i := range m.rows {
for j := 0; j < m.cols; j++ { for j := range m.cols {
t.set(j, i, m.get(i, j)) t.set(j, i, m.get(i, j))
} }
} }
@@ -295,10 +295,10 @@ func (m *matrix) mul(m2 *matrix) *matrix {
cols: m2.cols, cols: m2.cols,
data: make([]float32, m.rows*m2.cols), data: make([]float32, m.rows*m2.cols),
} }
for i := 0; i < mm.rows; i++ { for i := range mm.rows {
for j := 0; j < mm.cols; j++ { for j := range mm.cols {
var v float32 var v float32
for k := 0; k < m.rows; k++ { for k := range m.rows {
v += m.get(k, j) * m2.get(i, k) v += m.get(k, j) * m2.get(i, k)
} }
mm.set(i, j, v) mm.set(i, j, v)
@@ -309,8 +309,8 @@ func (m *matrix) mul(m2 *matrix) *matrix {
func (m *matrix) String() string { func (m *matrix) String() string {
var b strings.Builder var b strings.Builder
for i := 0; i < m.rows; i++ { for i := range m.rows {
for j := 0; j < m.cols; j++ { for j := range m.cols {
v := m.get(i, j) v := m.get(i, j)
b.WriteString(strconv.FormatFloat(float64(v), 'g', -1, 32)) b.WriteString(strconv.FormatFloat(float64(v), 'g', -1, 32))
b.WriteString(", ") b.WriteString(", ")
+95
View File
@@ -247,9 +247,11 @@ func (f *Functions) getExtension(name string) js.Value {
func (f *Functions) ActiveTexture(t Enum) { func (f *Functions) ActiveTexture(t Enum) {
f._activeTexture.Invoke(int(t)) f._activeTexture.Invoke(int(t))
} }
func (f *Functions) AttachShader(p Program, s Shader) { func (f *Functions) AttachShader(p Program, s Shader) {
f._attachShader.Invoke(js.Value(p), js.Value(s)) f._attachShader.Invoke(js.Value(p), js.Value(s))
} }
func (f *Functions) BeginQuery(target Enum, query Query) { func (f *Functions) BeginQuery(target Enum, query Query) {
if !f.EXT_disjoint_timer_query_webgl2.IsNull() { if !f.EXT_disjoint_timer_query_webgl2.IsNull() {
f._beginQuery.Invoke(int(target), js.Value(query)) f._beginQuery.Invoke(int(target), js.Value(query))
@@ -257,36 +259,47 @@ func (f *Functions) BeginQuery(target Enum, query Query) {
f.EXT_disjoint_timer_query.Call("beginQueryEXT", int(target), js.Value(query)) f.EXT_disjoint_timer_query.Call("beginQueryEXT", int(target), js.Value(query))
} }
} }
func (f *Functions) BindAttribLocation(p Program, a Attrib, name string) { func (f *Functions) BindAttribLocation(p Program, a Attrib, name string) {
f._bindAttribLocation.Invoke(js.Value(p), int(a), name) f._bindAttribLocation.Invoke(js.Value(p), int(a), name)
} }
func (f *Functions) BindBuffer(target Enum, b Buffer) { func (f *Functions) BindBuffer(target Enum, b Buffer) {
f._bindBuffer.Invoke(int(target), js.Value(b)) f._bindBuffer.Invoke(int(target), js.Value(b))
} }
func (f *Functions) BindBufferBase(target Enum, index int, b Buffer) { func (f *Functions) BindBufferBase(target Enum, index int, b Buffer) {
f._bindBufferBase.Invoke(int(target), index, js.Value(b)) f._bindBufferBase.Invoke(int(target), index, js.Value(b))
} }
func (f *Functions) BindFramebuffer(target Enum, fb Framebuffer) { func (f *Functions) BindFramebuffer(target Enum, fb Framebuffer) {
f._bindFramebuffer.Invoke(int(target), js.Value(fb)) f._bindFramebuffer.Invoke(int(target), js.Value(fb))
} }
func (f *Functions) BindRenderbuffer(target Enum, rb Renderbuffer) { func (f *Functions) BindRenderbuffer(target Enum, rb Renderbuffer) {
f._bindRenderbuffer.Invoke(int(target), js.Value(rb)) f._bindRenderbuffer.Invoke(int(target), js.Value(rb))
} }
func (f *Functions) BindTexture(target Enum, t Texture) { func (f *Functions) BindTexture(target Enum, t Texture) {
f._bindTexture.Invoke(int(target), js.Value(t)) 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) { func (f *Functions) BindImageTexture(unit int, t Texture, level int, layered bool, layer int, access, format Enum) {
panic("not implemented") panic("not implemented")
} }
func (f *Functions) BindVertexArray(a VertexArray) { func (f *Functions) BindVertexArray(a VertexArray) {
panic("not supported") panic("not supported")
} }
func (f *Functions) BlendEquation(mode Enum) { func (f *Functions) BlendEquation(mode Enum) {
f._blendEquation.Invoke(int(mode)) f._blendEquation.Invoke(int(mode))
} }
func (f *Functions) BlendFuncSeparate(srcRGB, dstRGB, srcA, dstA Enum) { func (f *Functions) BlendFuncSeparate(srcRGB, dstRGB, srcA, dstA Enum) {
f._blendFunc.Invoke(int(srcRGB), int(dstRGB), int(srcA), int(dstA)) f._blendFunc.Invoke(int(srcRGB), int(dstRGB), int(srcA), int(dstA))
} }
func (f *Functions) BufferData(target Enum, size int, usage Enum, data []byte) { func (f *Functions) BufferData(target Enum, size int, usage Enum, data []byte) {
if data == nil { if data == nil {
f._bufferData.Invoke(int(target), size, int(usage)) f._bufferData.Invoke(int(target), size, int(usage))
@@ -297,9 +310,11 @@ func (f *Functions) BufferData(target Enum, size int, usage Enum, data []byte) {
f._bufferData.Invoke(int(target), f.byteArrayOf(data), int(usage)) f._bufferData.Invoke(int(target), f.byteArrayOf(data), int(usage))
} }
} }
func (f *Functions) BufferSubData(target Enum, offset int, src []byte) { func (f *Functions) BufferSubData(target Enum, offset int, src []byte) {
f._bufferSubData.Invoke(int(target), offset, f.byteArrayOf(src)) f._bufferSubData.Invoke(int(target), offset, f.byteArrayOf(src))
} }
func (f *Functions) CheckFramebufferStatus(target Enum) Enum { func (f *Functions) CheckFramebufferStatus(target Enum) Enum {
status := Enum(f._checkFramebufferStatus.Invoke(int(target)).Int()) status := Enum(f._checkFramebufferStatus.Invoke(int(target)).Int())
if status != FRAMEBUFFER_COMPLETE && f.Ctx.Call("isContextLost").Bool() { if status != FRAMEBUFFER_COMPLETE && f.Ctx.Call("isContextLost").Bool() {
@@ -308,54 +323,71 @@ func (f *Functions) CheckFramebufferStatus(target Enum) Enum {
} }
return status return status
} }
func (f *Functions) Clear(mask Enum) { func (f *Functions) Clear(mask Enum) {
f._clear.Invoke(int(mask)) f._clear.Invoke(int(mask))
} }
func (f *Functions) ClearColor(red, green, blue, alpha float32) { func (f *Functions) ClearColor(red, green, blue, alpha float32) {
f._clearColor.Invoke(red, green, blue, alpha) f._clearColor.Invoke(red, green, blue, alpha)
} }
func (f *Functions) ClearDepthf(d float32) { func (f *Functions) ClearDepthf(d float32) {
f._clearDepth.Invoke(d) f._clearDepth.Invoke(d)
} }
func (f *Functions) CompileShader(s Shader) { func (f *Functions) CompileShader(s Shader) {
f._compileShader.Invoke(js.Value(s)) f._compileShader.Invoke(js.Value(s))
} }
func (f *Functions) CopyTexSubImage2D(target Enum, level, xoffset, yoffset, x, y, width, height int) { 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) f._copyTexSubImage2D.Invoke(int(target), level, xoffset, yoffset, x, y, width, height)
} }
func (f *Functions) CreateBuffer() Buffer { func (f *Functions) CreateBuffer() Buffer {
return Buffer(f._createBuffer.Invoke()) return Buffer(f._createBuffer.Invoke())
} }
func (f *Functions) CreateFramebuffer() Framebuffer { func (f *Functions) CreateFramebuffer() Framebuffer {
return Framebuffer(f._createFramebuffer.Invoke()) return Framebuffer(f._createFramebuffer.Invoke())
} }
func (f *Functions) CreateProgram() Program { func (f *Functions) CreateProgram() Program {
return Program(f._createProgram.Invoke()) return Program(f._createProgram.Invoke())
} }
func (f *Functions) CreateQuery() Query { func (f *Functions) CreateQuery() Query {
return Query(f._createQuery.Invoke()) return Query(f._createQuery.Invoke())
} }
func (f *Functions) CreateRenderbuffer() Renderbuffer { func (f *Functions) CreateRenderbuffer() Renderbuffer {
return Renderbuffer(f._createRenderbuffer.Invoke()) return Renderbuffer(f._createRenderbuffer.Invoke())
} }
func (f *Functions) CreateShader(ty Enum) Shader { func (f *Functions) CreateShader(ty Enum) Shader {
return Shader(f._createShader.Invoke(int(ty))) return Shader(f._createShader.Invoke(int(ty)))
} }
func (f *Functions) CreateTexture() Texture { func (f *Functions) CreateTexture() Texture {
return Texture(f._createTexture.Invoke()) return Texture(f._createTexture.Invoke())
} }
func (f *Functions) CreateVertexArray() VertexArray { func (f *Functions) CreateVertexArray() VertexArray {
panic("not supported") panic("not supported")
} }
func (f *Functions) DeleteBuffer(v Buffer) { func (f *Functions) DeleteBuffer(v Buffer) {
f._deleteBuffer.Invoke(js.Value(v)) f._deleteBuffer.Invoke(js.Value(v))
} }
func (f *Functions) DeleteFramebuffer(v Framebuffer) { func (f *Functions) DeleteFramebuffer(v Framebuffer) {
f._deleteFramebuffer.Invoke(js.Value(v)) f._deleteFramebuffer.Invoke(js.Value(v))
} }
func (f *Functions) DeleteProgram(p Program) { func (f *Functions) DeleteProgram(p Program) {
f._deleteProgram.Invoke(js.Value(p)) f._deleteProgram.Invoke(js.Value(p))
} }
func (f *Functions) DeleteQuery(query Query) { func (f *Functions) DeleteQuery(query Query) {
if !f.EXT_disjoint_timer_query_webgl2.IsNull() { if !f.EXT_disjoint_timer_query_webgl2.IsNull() {
f._deleteQuery.Invoke(js.Value(query)) f._deleteQuery.Invoke(js.Value(query))
@@ -363,45 +395,59 @@ func (f *Functions) DeleteQuery(query Query) {
f.EXT_disjoint_timer_query.Call("deleteQueryEXT", js.Value(query)) f.EXT_disjoint_timer_query.Call("deleteQueryEXT", js.Value(query))
} }
} }
func (f *Functions) DeleteShader(s Shader) { func (f *Functions) DeleteShader(s Shader) {
f._deleteShader.Invoke(js.Value(s)) f._deleteShader.Invoke(js.Value(s))
} }
func (f *Functions) DeleteRenderbuffer(v Renderbuffer) { func (f *Functions) DeleteRenderbuffer(v Renderbuffer) {
f._deleteRenderbuffer.Invoke(js.Value(v)) f._deleteRenderbuffer.Invoke(js.Value(v))
} }
func (f *Functions) DeleteTexture(v Texture) { func (f *Functions) DeleteTexture(v Texture) {
f._deleteTexture.Invoke(js.Value(v)) f._deleteTexture.Invoke(js.Value(v))
} }
func (f *Functions) DeleteVertexArray(a VertexArray) { func (f *Functions) DeleteVertexArray(a VertexArray) {
panic("not implemented") panic("not implemented")
} }
func (f *Functions) DepthFunc(fn Enum) { func (f *Functions) DepthFunc(fn Enum) {
f._depthFunc.Invoke(int(fn)) f._depthFunc.Invoke(int(fn))
} }
func (f *Functions) DepthMask(mask bool) { func (f *Functions) DepthMask(mask bool) {
f._depthMask.Invoke(mask) f._depthMask.Invoke(mask)
} }
func (f *Functions) DisableVertexAttribArray(a Attrib) { func (f *Functions) DisableVertexAttribArray(a Attrib) {
f._disableVertexAttribArray.Invoke(int(a)) f._disableVertexAttribArray.Invoke(int(a))
} }
func (f *Functions) Disable(cap Enum) { func (f *Functions) Disable(cap Enum) {
f._disable.Invoke(int(cap)) f._disable.Invoke(int(cap))
} }
func (f *Functions) DrawArrays(mode Enum, first, count int) { func (f *Functions) DrawArrays(mode Enum, first, count int) {
f._drawArrays.Invoke(int(mode), first, count) f._drawArrays.Invoke(int(mode), first, count)
} }
func (f *Functions) DrawElements(mode Enum, count int, ty Enum, offset int) { func (f *Functions) DrawElements(mode Enum, count int, ty Enum, offset int) {
f._drawElements.Invoke(int(mode), count, int(ty), offset) f._drawElements.Invoke(int(mode), count, int(ty), offset)
} }
func (f *Functions) DispatchCompute(x, y, z int) { func (f *Functions) DispatchCompute(x, y, z int) {
panic("not implemented") panic("not implemented")
} }
func (f *Functions) Enable(cap Enum) { func (f *Functions) Enable(cap Enum) {
f._enable.Invoke(int(cap)) f._enable.Invoke(int(cap))
} }
func (f *Functions) EnableVertexAttribArray(a Attrib) { func (f *Functions) EnableVertexAttribArray(a Attrib) {
f._enableVertexAttribArray.Invoke(int(a)) f._enableVertexAttribArray.Invoke(int(a))
} }
func (f *Functions) EndQuery(target Enum) { func (f *Functions) EndQuery(target Enum) {
if !f.EXT_disjoint_timer_query_webgl2.IsNull() { if !f.EXT_disjoint_timer_query_webgl2.IsNull() {
f._endQuery.Invoke(int(target)) f._endQuery.Invoke(int(target))
@@ -409,28 +455,36 @@ func (f *Functions) EndQuery(target Enum) {
f.EXT_disjoint_timer_query.Call("endQueryEXT", int(target)) f.EXT_disjoint_timer_query.Call("endQueryEXT", int(target))
} }
} }
func (f *Functions) Finish() { func (f *Functions) Finish() {
f._finish.Invoke() f._finish.Invoke()
} }
func (f *Functions) Flush() { func (f *Functions) Flush() {
f._flush.Invoke() f._flush.Invoke()
} }
func (f *Functions) FramebufferRenderbuffer(target, attachment, renderbuffertarget Enum, renderbuffer Renderbuffer) { func (f *Functions) FramebufferRenderbuffer(target, attachment, renderbuffertarget Enum, renderbuffer Renderbuffer) {
f._framebufferRenderbuffer.Invoke(int(target), int(attachment), int(renderbuffertarget), js.Value(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) { 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) f._framebufferTexture2D.Invoke(int(target), int(attachment), int(texTarget), js.Value(t), level)
} }
func (f *Functions) GenerateMipmap(target Enum) { func (f *Functions) GenerateMipmap(target Enum) {
f._generateMipmap.Invoke(int(target)) f._generateMipmap.Invoke(int(target))
} }
func (f *Functions) GetError() Enum { func (f *Functions) GetError() Enum {
// Avoid slow getError calls. See gio#179. // Avoid slow getError calls. See gio#179.
return 0 return 0
} }
func (f *Functions) GetRenderbufferParameteri(target, pname Enum) int { func (f *Functions) GetRenderbufferParameteri(target, pname Enum) int {
return paramVal(f._getRenderbufferParameteri.Invoke(int(pname))) return paramVal(f._getRenderbufferParameteri.Invoke(int(pname)))
} }
func (f *Functions) GetFramebufferAttachmentParameteri(target, attachment, pname Enum) int { func (f *Functions) GetFramebufferAttachmentParameteri(target, attachment, pname Enum) int {
if !f.isWebGL2 && pname == FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING { if !f.isWebGL2 && pname == FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING {
// FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING is only available on WebGL 2 // FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING is only available on WebGL 2
@@ -438,6 +492,7 @@ func (f *Functions) GetFramebufferAttachmentParameteri(target, attachment, pname
} }
return paramVal(f._getFramebufferAttachmentParameter.Invoke(int(target), int(attachment), int(pname))) return paramVal(f._getFramebufferAttachmentParameter.Invoke(int(target), int(attachment), int(pname)))
} }
func (f *Functions) GetBinding(pname Enum) Object { func (f *Functions) GetBinding(pname Enum) Object {
obj := f._getParameter.Invoke(int(pname)) obj := f._getParameter.Invoke(int(pname))
if !obj.Truthy() { if !obj.Truthy() {
@@ -445,6 +500,7 @@ func (f *Functions) GetBinding(pname Enum) Object {
} }
return Object(obj) return Object(obj)
} }
func (f *Functions) GetBindingi(pname Enum, idx int) Object { func (f *Functions) GetBindingi(pname Enum, idx int) Object {
obj := f._getIndexedParameter.Invoke(int(pname), idx) obj := f._getIndexedParameter.Invoke(int(pname), idx)
if !obj.Truthy() { if !obj.Truthy() {
@@ -452,6 +508,7 @@ func (f *Functions) GetBindingi(pname Enum, idx int) Object {
} }
return Object(obj) return Object(obj)
} }
func (f *Functions) GetInteger(pname Enum) int { func (f *Functions) GetInteger(pname Enum) int {
if !f.isWebGL2 { if !f.isWebGL2 {
switch pname { switch pname {
@@ -461,9 +518,11 @@ func (f *Functions) GetInteger(pname Enum) int {
} }
return paramVal(f._getParameter.Invoke(int(pname))) return paramVal(f._getParameter.Invoke(int(pname)))
} }
func (f *Functions) GetFloat(pname Enum) float32 { func (f *Functions) GetFloat(pname Enum) float32 {
return float32(f._getParameter.Invoke(int(pname)).Float()) return float32(f._getParameter.Invoke(int(pname)).Float())
} }
func (f *Functions) GetInteger4(pname Enum) [4]int { func (f *Functions) GetInteger4(pname Enum) [4]int {
arr := f._getParameter.Invoke(int(pname)) arr := f._getParameter.Invoke(int(pname))
var res [4]int var res [4]int
@@ -472,6 +531,7 @@ func (f *Functions) GetInteger4(pname Enum) [4]int {
} }
return res return res
} }
func (f *Functions) GetFloat4(pname Enum) [4]float32 { func (f *Functions) GetFloat4(pname Enum) [4]float32 {
arr := f._getParameter.Invoke(int(pname)) arr := f._getParameter.Invoke(int(pname))
var res [4]float32 var res [4]float32
@@ -480,12 +540,15 @@ func (f *Functions) GetFloat4(pname Enum) [4]float32 {
} }
return res return res
} }
func (f *Functions) GetProgrami(p Program, pname Enum) int { func (f *Functions) GetProgrami(p Program, pname Enum) int {
return paramVal(f._getProgramParameter.Invoke(js.Value(p), int(pname))) return paramVal(f._getProgramParameter.Invoke(js.Value(p), int(pname)))
} }
func (f *Functions) GetProgramInfoLog(p Program) string { func (f *Functions) GetProgramInfoLog(p Program) string {
return f._getProgramInfoLog.Invoke(js.Value(p)).String() return f._getProgramInfoLog.Invoke(js.Value(p)).String()
} }
func (f *Functions) GetQueryObjectuiv(query Query, pname Enum) uint { func (f *Functions) GetQueryObjectuiv(query Query, pname Enum) uint {
if !f.EXT_disjoint_timer_query_webgl2.IsNull() { if !f.EXT_disjoint_timer_query_webgl2.IsNull() {
return uint(paramVal(f._getQueryParameter.Invoke(js.Value(query), int(pname)))) return uint(paramVal(f._getQueryParameter.Invoke(js.Value(query), int(pname))))
@@ -493,12 +556,15 @@ func (f *Functions) GetQueryObjectuiv(query Query, pname Enum) uint {
return uint(paramVal(f.EXT_disjoint_timer_query.Call("getQueryObjectEXT", js.Value(query), int(pname)))) return uint(paramVal(f.EXT_disjoint_timer_query.Call("getQueryObjectEXT", js.Value(query), int(pname))))
} }
} }
func (f *Functions) GetShaderi(s Shader, pname Enum) int { func (f *Functions) GetShaderi(s Shader, pname Enum) int {
return paramVal(f._getShaderParameter.Invoke(js.Value(s), int(pname))) return paramVal(f._getShaderParameter.Invoke(js.Value(s), int(pname)))
} }
func (f *Functions) GetShaderInfoLog(s Shader) string { func (f *Functions) GetShaderInfoLog(s Shader) string {
return f._getShaderInfoLog.Invoke(js.Value(s)).String() return f._getShaderInfoLog.Invoke(js.Value(s)).String()
} }
func (f *Functions) GetString(pname Enum) string { func (f *Functions) GetString(pname Enum) string {
switch pname { switch pname {
case EXTENSIONS: case EXTENSIONS:
@@ -512,15 +578,19 @@ func (f *Functions) GetString(pname Enum) string {
return f._getParameter.Invoke(int(pname)).String() return f._getParameter.Invoke(int(pname)).String()
} }
} }
func (f *Functions) GetUniformBlockIndex(p Program, name string) uint { func (f *Functions) GetUniformBlockIndex(p Program, name string) uint {
return uint(paramVal(f._getUniformBlockIndex.Invoke(js.Value(p), name))) return uint(paramVal(f._getUniformBlockIndex.Invoke(js.Value(p), name)))
} }
func (f *Functions) GetUniformLocation(p Program, name string) Uniform { func (f *Functions) GetUniformLocation(p Program, name string) Uniform {
return Uniform(f._getUniformLocation.Invoke(js.Value(p), name)) return Uniform(f._getUniformLocation.Invoke(js.Value(p), name))
} }
func (f *Functions) GetVertexAttrib(index int, pname Enum) int { func (f *Functions) GetVertexAttrib(index int, pname Enum) int {
return paramVal(f._getVertexAttrib.Invoke(index, int(pname))) return paramVal(f._getVertexAttrib.Invoke(index, int(pname)))
} }
func (f *Functions) GetVertexAttribBinding(index int, pname Enum) Object { func (f *Functions) GetVertexAttribBinding(index int, pname Enum) Object {
obj := f._getVertexAttrib.Invoke(index, int(pname)) obj := f._getVertexAttrib.Invoke(index, int(pname))
if !obj.Truthy() { if !obj.Truthy() {
@@ -528,9 +598,11 @@ func (f *Functions) GetVertexAttribBinding(index int, pname Enum) Object {
} }
return Object(obj) return Object(obj)
} }
func (f *Functions) GetVertexAttribPointer(index int, pname Enum) uintptr { func (f *Functions) GetVertexAttribPointer(index int, pname Enum) uintptr {
return uintptr(f._getVertexAttribOffset.Invoke(index, int(pname)).Int()) return uintptr(f._getVertexAttribOffset.Invoke(index, int(pname)).Int())
} }
func (f *Functions) InvalidateFramebuffer(target, attachment Enum) { func (f *Functions) InvalidateFramebuffer(target, attachment Enum) {
fn := f.Ctx.Get("invalidateFramebuffer") fn := f.Ctx.Get("invalidateFramebuffer")
if !fn.IsUndefined() { if !fn.IsUndefined() {
@@ -541,74 +613,97 @@ func (f *Functions) InvalidateFramebuffer(target, attachment Enum) {
f._invalidateFramebuffer.Invoke(int(target), f.int32Buf) f._invalidateFramebuffer.Invoke(int(target), f.int32Buf)
} }
} }
func (f *Functions) IsEnabled(cap Enum) bool { func (f *Functions) IsEnabled(cap Enum) bool {
return f._isEnabled.Invoke(int(cap)).Truthy() return f._isEnabled.Invoke(int(cap)).Truthy()
} }
func (f *Functions) LinkProgram(p Program) { func (f *Functions) LinkProgram(p Program) {
f._linkProgram.Invoke(js.Value(p)) f._linkProgram.Invoke(js.Value(p))
} }
func (f *Functions) PixelStorei(pname Enum, param int) { func (f *Functions) PixelStorei(pname Enum, param int) {
f._pixelStorei.Invoke(int(pname), param) f._pixelStorei.Invoke(int(pname), param)
} }
func (f *Functions) MemoryBarrier(barriers Enum) { func (f *Functions) MemoryBarrier(barriers Enum) {
panic("not implemented") panic("not implemented")
} }
func (f *Functions) MapBufferRange(target Enum, offset, length int, access Enum) []byte { func (f *Functions) MapBufferRange(target Enum, offset, length int, access Enum) []byte {
panic("not implemented") panic("not implemented")
} }
func (f *Functions) RenderbufferStorage(target, internalformat Enum, width, height int) { func (f *Functions) RenderbufferStorage(target, internalformat Enum, width, height int) {
f._renderbufferStorage.Invoke(int(target), int(internalformat), width, height) f._renderbufferStorage.Invoke(int(target), int(internalformat), width, height)
} }
func (f *Functions) ReadPixels(x, y, width, height int, format, ty Enum, data []byte) { func (f *Functions) ReadPixels(x, y, width, height int, format, ty Enum, data []byte) {
ba := f.byteArrayOf(data) ba := f.byteArrayOf(data)
f._readPixels.Invoke(x, y, width, height, int(format), int(ty), ba) f._readPixels.Invoke(x, y, width, height, int(format), int(ty), ba)
js.CopyBytesToGo(data, ba) js.CopyBytesToGo(data, ba)
} }
func (f *Functions) Scissor(x, y, width, height int32) { func (f *Functions) Scissor(x, y, width, height int32) {
f._scissor.Invoke(x, y, width, height) f._scissor.Invoke(x, y, width, height)
} }
func (f *Functions) ShaderSource(s Shader, src string) { func (f *Functions) ShaderSource(s Shader, src string) {
f._shaderSource.Invoke(js.Value(s), src) f._shaderSource.Invoke(js.Value(s), src)
} }
func (f *Functions) TexImage2D(target Enum, level int, internalFormat Enum, width, height int, format, ty Enum) { 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) 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) { func (f *Functions) TexStorage2D(target Enum, levels int, internalFormat Enum, width, height int) {
f._texStorage2D.Invoke(int(target), levels, int(internalFormat), width, height) 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) { 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)) 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) { func (f *Functions) TexParameteri(target, pname Enum, param int) {
f._texParameteri.Invoke(int(target), int(pname), int(param)) f._texParameteri.Invoke(int(target), int(pname), int(param))
} }
func (f *Functions) UniformBlockBinding(p Program, uniformBlockIndex uint, uniformBlockBinding uint) { func (f *Functions) UniformBlockBinding(p Program, uniformBlockIndex uint, uniformBlockBinding uint) {
f._uniformBlockBinding.Invoke(js.Value(p), int(uniformBlockIndex), int(uniformBlockBinding)) f._uniformBlockBinding.Invoke(js.Value(p), int(uniformBlockIndex), int(uniformBlockBinding))
} }
func (f *Functions) Uniform1f(dst Uniform, v float32) { func (f *Functions) Uniform1f(dst Uniform, v float32) {
f._uniform1f.Invoke(js.Value(dst), v) f._uniform1f.Invoke(js.Value(dst), v)
} }
func (f *Functions) Uniform1i(dst Uniform, v int) { func (f *Functions) Uniform1i(dst Uniform, v int) {
f._uniform1i.Invoke(js.Value(dst), v) f._uniform1i.Invoke(js.Value(dst), v)
} }
func (f *Functions) Uniform2f(dst Uniform, v0, v1 float32) { func (f *Functions) Uniform2f(dst Uniform, v0, v1 float32) {
f._uniform2f.Invoke(js.Value(dst), v0, v1) f._uniform2f.Invoke(js.Value(dst), v0, v1)
} }
func (f *Functions) Uniform3f(dst Uniform, v0, v1, v2 float32) { func (f *Functions) Uniform3f(dst Uniform, v0, v1, v2 float32) {
f._uniform3f.Invoke(js.Value(dst), v0, v1, v2) f._uniform3f.Invoke(js.Value(dst), v0, v1, v2)
} }
func (f *Functions) Uniform4f(dst Uniform, v0, v1, v2, v3 float32) { func (f *Functions) Uniform4f(dst Uniform, v0, v1, v2, v3 float32) {
f._uniform4f.Invoke(js.Value(dst), v0, v1, v2, v3) f._uniform4f.Invoke(js.Value(dst), v0, v1, v2, v3)
} }
func (f *Functions) UseProgram(p Program) { func (f *Functions) UseProgram(p Program) {
f._useProgram.Invoke(js.Value(p)) f._useProgram.Invoke(js.Value(p))
} }
func (f *Functions) UnmapBuffer(target Enum) bool { func (f *Functions) UnmapBuffer(target Enum) bool {
panic("not implemented") panic("not implemented")
} }
func (f *Functions) VertexAttribPointer(dst Attrib, size int, ty Enum, normalized bool, stride, offset int) { 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) f._vertexAttribPointer.Invoke(int(dst), size, int(ty), normalized, stride, offset)
} }
func (f *Functions) Viewport(x, y, width, height int) { func (f *Functions) Viewport(x, y, width, height int) {
f._viewport.Invoke(x, y, width, height) f._viewport.Invoke(x, y, width, height)
} }
+96 -2
View File
@@ -223,7 +223,7 @@ type Functions struct {
uintptrs [100]uintptr uintptrs [100]uintptr
} }
type Context interface{} type Context any
func NewFunctions(ctx Context, forceES bool) (*Functions, error) { func NewFunctions(ctx Context, forceES bool) (*Functions, error) {
if ctx != nil { if ctx != nil {
@@ -239,45 +239,58 @@ func NewFunctions(ctx Context, forceES bool) (*Functions, error) {
func (c *Functions) ActiveTexture(t Enum) { func (c *Functions) ActiveTexture(t Enum) {
syscall.Syscall(_glActiveTexture.Addr(), 1, uintptr(t), 0, 0) syscall.Syscall(_glActiveTexture.Addr(), 1, uintptr(t), 0, 0)
} }
func (c *Functions) AttachShader(p Program, s Shader) { func (c *Functions) AttachShader(p Program, s Shader) {
syscall.Syscall(_glAttachShader.Addr(), 2, uintptr(p.V), uintptr(s.V), 0) syscall.Syscall(_glAttachShader.Addr(), 2, uintptr(p.V), uintptr(s.V), 0)
} }
func (f *Functions) BeginQuery(target Enum, query Query) { func (f *Functions) BeginQuery(target Enum, query Query) {
syscall.Syscall(_glBeginQuery.Addr(), 2, uintptr(target), uintptr(query.V), 0) syscall.Syscall(_glBeginQuery.Addr(), 2, uintptr(target), uintptr(query.V), 0)
} }
func (c *Functions) BindAttribLocation(p Program, a Attrib, name string) { func (c *Functions) BindAttribLocation(p Program, a Attrib, name string) {
cname := cString(name) cname := cString(name)
c0 := &cname[0] c0 := &cname[0]
syscall.Syscall(_glBindAttribLocation.Addr(), 3, uintptr(p.V), uintptr(a), uintptr(unsafe.Pointer(c0))) syscall.Syscall(_glBindAttribLocation.Addr(), 3, uintptr(p.V), uintptr(a), uintptr(unsafe.Pointer(c0)))
issue34474KeepAlive(c) issue34474KeepAlive(c)
} }
func (c *Functions) BindBuffer(target Enum, b Buffer) { func (c *Functions) BindBuffer(target Enum, b Buffer) {
syscall.Syscall(_glBindBuffer.Addr(), 2, uintptr(target), uintptr(b.V), 0) syscall.Syscall(_glBindBuffer.Addr(), 2, uintptr(target), uintptr(b.V), 0)
} }
func (c *Functions) BindBufferBase(target Enum, index int, b Buffer) { func (c *Functions) BindBufferBase(target Enum, index int, b Buffer) {
syscall.Syscall(_glBindBufferBase.Addr(), 3, uintptr(target), uintptr(index), uintptr(b.V)) syscall.Syscall(_glBindBufferBase.Addr(), 3, uintptr(target), uintptr(index), uintptr(b.V))
} }
func (c *Functions) BindFramebuffer(target Enum, fb Framebuffer) { func (c *Functions) BindFramebuffer(target Enum, fb Framebuffer) {
syscall.Syscall(_glBindFramebuffer.Addr(), 2, uintptr(target), uintptr(fb.V), 0) syscall.Syscall(_glBindFramebuffer.Addr(), 2, uintptr(target), uintptr(fb.V), 0)
} }
func (c *Functions) BindRenderbuffer(target Enum, rb Renderbuffer) { func (c *Functions) BindRenderbuffer(target Enum, rb Renderbuffer) {
syscall.Syscall(_glBindRenderbuffer.Addr(), 2, uintptr(target), uintptr(rb.V), 0) 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) { func (f *Functions) BindImageTexture(unit int, t Texture, level int, layered bool, layer int, access, format Enum) {
panic("not implemented") panic("not implemented")
} }
func (c *Functions) BindTexture(target Enum, t Texture) { func (c *Functions) BindTexture(target Enum, t Texture) {
syscall.Syscall(_glBindTexture.Addr(), 2, uintptr(target), uintptr(t.V), 0) syscall.Syscall(_glBindTexture.Addr(), 2, uintptr(target), uintptr(t.V), 0)
} }
func (c *Functions) BindVertexArray(a VertexArray) { func (c *Functions) BindVertexArray(a VertexArray) {
syscall.Syscall(_glBindVertexArray.Addr(), 1, uintptr(a.V), 0, 0) syscall.Syscall(_glBindVertexArray.Addr(), 1, uintptr(a.V), 0, 0)
} }
func (c *Functions) BlendEquation(mode Enum) { func (c *Functions) BlendEquation(mode Enum) {
syscall.Syscall(_glBlendEquation.Addr(), 1, uintptr(mode), 0, 0) syscall.Syscall(_glBlendEquation.Addr(), 1, uintptr(mode), 0, 0)
} }
func (c *Functions) BlendFuncSeparate(srcRGB, dstRGB, srcA, dstA Enum) { func (c *Functions) BlendFuncSeparate(srcRGB, dstRGB, srcA, dstA Enum) {
syscall.Syscall6(_glBlendFuncSeparate.Addr(), 4, uintptr(srcRGB), uintptr(dstRGB), uintptr(srcA), uintptr(dstA), 0, 0) 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) { func (c *Functions) BufferData(target Enum, size int, usage Enum, data []byte) {
var p unsafe.Pointer var p unsafe.Pointer
if len(data) > 0 { if len(data) > 0 {
@@ -285,6 +298,7 @@ func (c *Functions) BufferData(target Enum, size int, usage Enum, data []byte) {
} }
syscall.Syscall6(_glBufferData.Addr(), 4, uintptr(target), uintptr(size), uintptr(p), uintptr(usage), 0, 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) { func (f *Functions) BufferSubData(target Enum, offset int, src []byte) {
if n := len(src); n > 0 { if n := len(src); n > 0 {
s0 := &src[0] s0 := &src[0]
@@ -292,93 +306,118 @@ func (f *Functions) BufferSubData(target Enum, offset int, src []byte) {
issue34474KeepAlive(s0) issue34474KeepAlive(s0)
} }
} }
func (c *Functions) CheckFramebufferStatus(target Enum) Enum { func (c *Functions) CheckFramebufferStatus(target Enum) Enum {
s, _, _ := syscall.Syscall(_glCheckFramebufferStatus.Addr(), 1, uintptr(target), 0, 0) s, _, _ := syscall.Syscall(_glCheckFramebufferStatus.Addr(), 1, uintptr(target), 0, 0)
return Enum(s) return Enum(s)
} }
func (c *Functions) Clear(mask Enum) { func (c *Functions) Clear(mask Enum) {
syscall.Syscall(_glClear.Addr(), 1, uintptr(mask), 0, 0) syscall.Syscall(_glClear.Addr(), 1, uintptr(mask), 0, 0)
} }
func (c *Functions) ClearColor(red, green, blue, alpha float32) { 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) 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) { func (c *Functions) ClearDepthf(d float32) {
syscall.Syscall(_glClearDepthf.Addr(), 1, uintptr(math.Float32bits(d)), 0, 0) syscall.Syscall(_glClearDepthf.Addr(), 1, uintptr(math.Float32bits(d)), 0, 0)
} }
func (c *Functions) CompileShader(s Shader) { func (c *Functions) CompileShader(s Shader) {
syscall.Syscall(_glCompileShader.Addr(), 1, uintptr(s.V), 0, 0) syscall.Syscall(_glCompileShader.Addr(), 1, uintptr(s.V), 0, 0)
} }
func (f *Functions) CopyTexSubImage2D(target Enum, level, xoffset, yoffset, x, y, width, height int) { 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) 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) { func (f *Functions) GenerateMipmap(target Enum) {
syscall.Syscall(_glGenerateMipmap.Addr(), 1, uintptr(target), 0, 0) syscall.Syscall(_glGenerateMipmap.Addr(), 1, uintptr(target), 0, 0)
} }
func (c *Functions) CreateBuffer() Buffer { func (c *Functions) CreateBuffer() Buffer {
var buf uintptr var buf uintptr
syscall.Syscall(_glGenBuffers.Addr(), 2, 1, uintptr(unsafe.Pointer(&buf)), 0) syscall.Syscall(_glGenBuffers.Addr(), 2, 1, uintptr(unsafe.Pointer(&buf)), 0)
return Buffer{uint(buf)} return Buffer{uint(buf)}
} }
func (c *Functions) CreateFramebuffer() Framebuffer { func (c *Functions) CreateFramebuffer() Framebuffer {
var fb uintptr var fb uintptr
syscall.Syscall(_glGenFramebuffers.Addr(), 2, 1, uintptr(unsafe.Pointer(&fb)), 0) syscall.Syscall(_glGenFramebuffers.Addr(), 2, 1, uintptr(unsafe.Pointer(&fb)), 0)
return Framebuffer{uint(fb)} return Framebuffer{uint(fb)}
} }
func (c *Functions) CreateProgram() Program { func (c *Functions) CreateProgram() Program {
p, _, _ := syscall.Syscall(_glCreateProgram.Addr(), 0, 0, 0, 0) p, _, _ := syscall.Syscall(_glCreateProgram.Addr(), 0, 0, 0, 0)
return Program{uint(p)} return Program{uint(p)}
} }
func (f *Functions) CreateQuery() Query { func (f *Functions) CreateQuery() Query {
var q uintptr var q uintptr
syscall.Syscall(_glGenQueries.Addr(), 2, 1, uintptr(unsafe.Pointer(&q)), 0) syscall.Syscall(_glGenQueries.Addr(), 2, 1, uintptr(unsafe.Pointer(&q)), 0)
return Query{uint(q)} return Query{uint(q)}
} }
func (c *Functions) CreateRenderbuffer() Renderbuffer { func (c *Functions) CreateRenderbuffer() Renderbuffer {
var rb uintptr var rb uintptr
syscall.Syscall(_glGenRenderbuffers.Addr(), 2, 1, uintptr(unsafe.Pointer(&rb)), 0) syscall.Syscall(_glGenRenderbuffers.Addr(), 2, 1, uintptr(unsafe.Pointer(&rb)), 0)
return Renderbuffer{uint(rb)} return Renderbuffer{uint(rb)}
} }
func (c *Functions) CreateShader(ty Enum) Shader { func (c *Functions) CreateShader(ty Enum) Shader {
s, _, _ := syscall.Syscall(_glCreateShader.Addr(), 1, uintptr(ty), 0, 0) s, _, _ := syscall.Syscall(_glCreateShader.Addr(), 1, uintptr(ty), 0, 0)
return Shader{uint(s)} return Shader{uint(s)}
} }
func (c *Functions) CreateTexture() Texture { func (c *Functions) CreateTexture() Texture {
var t uintptr var t uintptr
syscall.Syscall(_glGenTextures.Addr(), 2, 1, uintptr(unsafe.Pointer(&t)), 0) syscall.Syscall(_glGenTextures.Addr(), 2, 1, uintptr(unsafe.Pointer(&t)), 0)
return Texture{uint(t)} return Texture{uint(t)}
} }
func (c *Functions) CreateVertexArray() VertexArray { func (c *Functions) CreateVertexArray() VertexArray {
var t uintptr var t uintptr
syscall.Syscall(_glGenVertexArrays.Addr(), 2, 1, uintptr(unsafe.Pointer(&t)), 0) syscall.Syscall(_glGenVertexArrays.Addr(), 2, 1, uintptr(unsafe.Pointer(&t)), 0)
return VertexArray{uint(t)} return VertexArray{uint(t)}
} }
func (c *Functions) DeleteBuffer(v Buffer) { func (c *Functions) DeleteBuffer(v Buffer) {
syscall.Syscall(_glDeleteBuffers.Addr(), 2, 1, uintptr(unsafe.Pointer(&v)), 0) syscall.Syscall(_glDeleteBuffers.Addr(), 2, 1, uintptr(unsafe.Pointer(&v)), 0)
} }
func (c *Functions) DeleteFramebuffer(v Framebuffer) { func (c *Functions) DeleteFramebuffer(v Framebuffer) {
syscall.Syscall(_glDeleteFramebuffers.Addr(), 2, 1, uintptr(unsafe.Pointer(&v.V)), 0) syscall.Syscall(_glDeleteFramebuffers.Addr(), 2, 1, uintptr(unsafe.Pointer(&v.V)), 0)
} }
func (c *Functions) DeleteProgram(p Program) { func (c *Functions) DeleteProgram(p Program) {
syscall.Syscall(_glDeleteProgram.Addr(), 1, uintptr(p.V), 0, 0) syscall.Syscall(_glDeleteProgram.Addr(), 1, uintptr(p.V), 0, 0)
} }
func (f *Functions) DeleteQuery(query Query) { func (f *Functions) DeleteQuery(query Query) {
syscall.Syscall(_glDeleteQueries.Addr(), 2, 1, uintptr(unsafe.Pointer(&query.V)), 0) syscall.Syscall(_glDeleteQueries.Addr(), 2, 1, uintptr(unsafe.Pointer(&query.V)), 0)
} }
func (c *Functions) DeleteShader(s Shader) { func (c *Functions) DeleteShader(s Shader) {
syscall.Syscall(_glDeleteShader.Addr(), 1, uintptr(s.V), 0, 0) syscall.Syscall(_glDeleteShader.Addr(), 1, uintptr(s.V), 0, 0)
} }
func (c *Functions) DeleteRenderbuffer(v Renderbuffer) { func (c *Functions) DeleteRenderbuffer(v Renderbuffer) {
syscall.Syscall(_glDeleteRenderbuffers.Addr(), 2, 1, uintptr(unsafe.Pointer(&v.V)), 0) syscall.Syscall(_glDeleteRenderbuffers.Addr(), 2, 1, uintptr(unsafe.Pointer(&v.V)), 0)
} }
func (c *Functions) DeleteTexture(v Texture) { func (c *Functions) DeleteTexture(v Texture) {
syscall.Syscall(_glDeleteTextures.Addr(), 2, 1, uintptr(unsafe.Pointer(&v.V)), 0) syscall.Syscall(_glDeleteTextures.Addr(), 2, 1, uintptr(unsafe.Pointer(&v.V)), 0)
} }
func (f *Functions) DeleteVertexArray(array VertexArray) { func (f *Functions) DeleteVertexArray(array VertexArray) {
syscall.Syscall(_glDeleteVertexArrays.Addr(), 2, 1, uintptr(unsafe.Pointer(&array.V)), 0) syscall.Syscall(_glDeleteVertexArrays.Addr(), 2, 1, uintptr(unsafe.Pointer(&array.V)), 0)
} }
func (c *Functions) DepthFunc(f Enum) { func (c *Functions) DepthFunc(f Enum) {
syscall.Syscall(_glDepthFunc.Addr(), 1, uintptr(f), 0, 0) syscall.Syscall(_glDepthFunc.Addr(), 1, uintptr(f), 0, 0)
} }
func (c *Functions) DepthMask(mask bool) { func (c *Functions) DepthMask(mask bool) {
var m uintptr var m uintptr
if mask { if mask {
@@ -386,42 +425,55 @@ func (c *Functions) DepthMask(mask bool) {
} }
syscall.Syscall(_glDepthMask.Addr(), 1, m, 0, 0) syscall.Syscall(_glDepthMask.Addr(), 1, m, 0, 0)
} }
func (c *Functions) DisableVertexAttribArray(a Attrib) { func (c *Functions) DisableVertexAttribArray(a Attrib) {
syscall.Syscall(_glDisableVertexAttribArray.Addr(), 1, uintptr(a), 0, 0) syscall.Syscall(_glDisableVertexAttribArray.Addr(), 1, uintptr(a), 0, 0)
} }
func (c *Functions) Disable(cap Enum) { func (c *Functions) Disable(cap Enum) {
syscall.Syscall(_glDisable.Addr(), 1, uintptr(cap), 0, 0) syscall.Syscall(_glDisable.Addr(), 1, uintptr(cap), 0, 0)
} }
func (c *Functions) DrawArrays(mode Enum, first, count int) { func (c *Functions) DrawArrays(mode Enum, first, count int) {
syscall.Syscall(_glDrawArrays.Addr(), 3, uintptr(mode), uintptr(first), uintptr(count)) syscall.Syscall(_glDrawArrays.Addr(), 3, uintptr(mode), uintptr(first), uintptr(count))
} }
func (c *Functions) DrawElements(mode Enum, count int, ty Enum, offset int) { 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) syscall.Syscall6(_glDrawElements.Addr(), 4, uintptr(mode), uintptr(count), uintptr(ty), uintptr(offset), 0, 0)
} }
func (f *Functions) DispatchCompute(x, y, z int) { func (f *Functions) DispatchCompute(x, y, z int) {
panic("not implemented") panic("not implemented")
} }
func (c *Functions) Enable(cap Enum) { func (c *Functions) Enable(cap Enum) {
syscall.Syscall(_glEnable.Addr(), 1, uintptr(cap), 0, 0) syscall.Syscall(_glEnable.Addr(), 1, uintptr(cap), 0, 0)
} }
func (c *Functions) EnableVertexAttribArray(a Attrib) { func (c *Functions) EnableVertexAttribArray(a Attrib) {
syscall.Syscall(_glEnableVertexAttribArray.Addr(), 1, uintptr(a), 0, 0) syscall.Syscall(_glEnableVertexAttribArray.Addr(), 1, uintptr(a), 0, 0)
} }
func (f *Functions) EndQuery(target Enum) { func (f *Functions) EndQuery(target Enum) {
syscall.Syscall(_glEndQuery.Addr(), 1, uintptr(target), 0, 0) syscall.Syscall(_glEndQuery.Addr(), 1, uintptr(target), 0, 0)
} }
func (c *Functions) Finish() { func (c *Functions) Finish() {
syscall.Syscall(_glFinish.Addr(), 0, 0, 0, 0) syscall.Syscall(_glFinish.Addr(), 0, 0, 0, 0)
} }
func (c *Functions) Flush() { func (c *Functions) Flush() {
syscall.Syscall(_glFlush.Addr(), 0, 0, 0, 0) syscall.Syscall(_glFlush.Addr(), 0, 0, 0, 0)
} }
func (c *Functions) FramebufferRenderbuffer(target, attachment, renderbuffertarget Enum, renderbuffer Renderbuffer) { 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) 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) { 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) 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 { func (f *Functions) GetUniformBlockIndex(p Program, name string) uint {
cname := cString(name) cname := cString(name)
c0 := &cname[0] c0 := &cname[0]
@@ -429,24 +481,30 @@ func (f *Functions) GetUniformBlockIndex(p Program, name string) uint {
issue34474KeepAlive(c0) issue34474KeepAlive(c0)
return uint(u) return uint(u)
} }
func (c *Functions) GetBinding(pname Enum) Object { func (c *Functions) GetBinding(pname Enum) Object {
return Object{uint(c.GetInteger(pname))} return Object{uint(c.GetInteger(pname))}
} }
func (c *Functions) GetBindingi(pname Enum, idx int) Object { func (c *Functions) GetBindingi(pname Enum, idx int) Object {
return Object{uint(c.GetIntegeri(pname, idx))} return Object{uint(c.GetIntegeri(pname, idx))}
} }
func (c *Functions) GetError() Enum { func (c *Functions) GetError() Enum {
e, _, _ := syscall.Syscall(_glGetError.Addr(), 0, 0, 0, 0) e, _, _ := syscall.Syscall(_glGetError.Addr(), 0, 0, 0, 0)
return Enum(e) return Enum(e)
} }
func (c *Functions) GetRenderbufferParameteri(target, pname Enum) int { func (c *Functions) GetRenderbufferParameteri(target, pname Enum) int {
syscall.Syscall(_glGetRenderbufferParameteriv.Addr(), 3, uintptr(target), uintptr(pname), uintptr(unsafe.Pointer(&c.int32s[0]))) syscall.Syscall(_glGetRenderbufferParameteriv.Addr(), 3, uintptr(target), uintptr(pname), uintptr(unsafe.Pointer(&c.int32s[0])))
return int(c.int32s[0]) return int(c.int32s[0])
} }
func (c *Functions) GetFramebufferAttachmentParameteri(target, attachment, pname Enum) int { 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) syscall.Syscall6(_glGetFramebufferAttachmentParameteriv.Addr(), 4, uintptr(target), uintptr(attachment), uintptr(pname), uintptr(unsafe.Pointer(&c.int32s[0])), 0, 0)
return int(c.int32s[0]) return int(c.int32s[0])
} }
func (c *Functions) GetInteger4(pname Enum) [4]int { func (c *Functions) GetInteger4(pname Enum) [4]int {
syscall.Syscall(_glGetIntegerv.Addr(), 2, uintptr(pname), uintptr(unsafe.Pointer(&c.int32s[0])), 0) syscall.Syscall(_glGetIntegerv.Addr(), 2, uintptr(pname), uintptr(unsafe.Pointer(&c.int32s[0])), 0)
var r [4]int var r [4]int
@@ -455,28 +513,34 @@ func (c *Functions) GetInteger4(pname Enum) [4]int {
} }
return r return r
} }
func (c *Functions) GetInteger(pname Enum) int { func (c *Functions) GetInteger(pname Enum) int {
syscall.Syscall(_glGetIntegerv.Addr(), 2, uintptr(pname), uintptr(unsafe.Pointer(&c.int32s[0])), 0) syscall.Syscall(_glGetIntegerv.Addr(), 2, uintptr(pname), uintptr(unsafe.Pointer(&c.int32s[0])), 0)
return int(c.int32s[0]) return int(c.int32s[0])
} }
func (c *Functions) GetIntegeri(pname Enum, idx int) int { 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]))) syscall.Syscall(_glGetIntegeri_v.Addr(), 3, uintptr(pname), uintptr(idx), uintptr(unsafe.Pointer(&c.int32s[0])))
return int(c.int32s[0]) return int(c.int32s[0])
} }
func (c *Functions) GetFloat(pname Enum) float32 { func (c *Functions) GetFloat(pname Enum) float32 {
syscall.Syscall(_glGetFloatv.Addr(), 2, uintptr(pname), uintptr(unsafe.Pointer(&c.float32s[0])), 0) syscall.Syscall(_glGetFloatv.Addr(), 2, uintptr(pname), uintptr(unsafe.Pointer(&c.float32s[0])), 0)
return c.float32s[0] return c.float32s[0]
} }
func (c *Functions) GetFloat4(pname Enum) [4]float32 { func (c *Functions) GetFloat4(pname Enum) [4]float32 {
syscall.Syscall(_glGetFloatv.Addr(), 2, uintptr(pname), uintptr(unsafe.Pointer(&c.float32s[0])), 0) syscall.Syscall(_glGetFloatv.Addr(), 2, uintptr(pname), uintptr(unsafe.Pointer(&c.float32s[0])), 0)
var r [4]float32 var r [4]float32
copy(r[:], c.float32s[:]) copy(r[:], c.float32s[:])
return r return r
} }
func (c *Functions) GetProgrami(p Program, pname Enum) int { 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]))) syscall.Syscall(_glGetProgramiv.Addr(), 3, uintptr(p.V), uintptr(pname), uintptr(unsafe.Pointer(&c.int32s[0])))
return int(c.int32s[0]) return int(c.int32s[0])
} }
func (c *Functions) GetProgramInfoLog(p Program) string { func (c *Functions) GetProgramInfoLog(p Program) string {
n := c.GetProgrami(p, INFO_LOG_LENGTH) n := c.GetProgrami(p, INFO_LOG_LENGTH)
if n == 0 { if n == 0 {
@@ -486,24 +550,29 @@ func (c *Functions) GetProgramInfoLog(p Program) string {
syscall.Syscall6(_glGetProgramInfoLog.Addr(), 4, uintptr(p.V), uintptr(len(buf)), 0, uintptr(unsafe.Pointer(&buf[0])), 0, 0) syscall.Syscall6(_glGetProgramInfoLog.Addr(), 4, uintptr(p.V), uintptr(len(buf)), 0, uintptr(unsafe.Pointer(&buf[0])), 0, 0)
return string(buf) return string(buf)
} }
func (c *Functions) GetQueryObjectuiv(query Query, pname Enum) uint { 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]))) syscall.Syscall(_glGetQueryObjectuiv.Addr(), 3, uintptr(query.V), uintptr(pname), uintptr(unsafe.Pointer(&c.int32s[0])))
return uint(c.int32s[0]) return uint(c.int32s[0])
} }
func (c *Functions) GetShaderi(s Shader, pname Enum) int { 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]))) syscall.Syscall(_glGetShaderiv.Addr(), 3, uintptr(s.V), uintptr(pname), uintptr(unsafe.Pointer(&c.int32s[0])))
return int(c.int32s[0]) return int(c.int32s[0])
} }
func (c *Functions) GetShaderInfoLog(s Shader) string { func (c *Functions) GetShaderInfoLog(s Shader) string {
n := c.GetShaderi(s, INFO_LOG_LENGTH) n := c.GetShaderi(s, INFO_LOG_LENGTH)
buf := make([]byte, n) buf := make([]byte, n)
syscall.Syscall6(_glGetShaderInfoLog.Addr(), 4, uintptr(s.V), uintptr(len(buf)), 0, uintptr(unsafe.Pointer(&buf[0])), 0, 0) syscall.Syscall6(_glGetShaderInfoLog.Addr(), 4, uintptr(s.V), uintptr(len(buf)), 0, uintptr(unsafe.Pointer(&buf[0])), 0, 0)
return string(buf) return string(buf)
} }
func (c *Functions) GetString(pname Enum) string { func (c *Functions) GetString(pname Enum) string {
s, _, _ := syscall.Syscall(_glGetString.Addr(), 1, uintptr(pname), 0, 0) s, _, _ := syscall.Syscall(_glGetString.Addr(), 1, uintptr(pname), 0, 0)
return windows.BytePtrToString((*byte)(unsafe.Pointer(s))) return windows.BytePtrToString((*byte)(unsafe.Pointer(s)))
} }
func (c *Functions) GetUniformLocation(p Program, name string) Uniform { func (c *Functions) GetUniformLocation(p Program, name string) Uniform {
cname := cString(name) cname := cString(name)
c0 := &cname[0] c0 := &cname[0]
@@ -511,6 +580,7 @@ func (c *Functions) GetUniformLocation(p Program, name string) Uniform {
issue34474KeepAlive(c0) issue34474KeepAlive(c0)
return Uniform{int(u)} return Uniform{int(u)}
} }
func (c *Functions) GetVertexAttrib(index int, pname Enum) int { func (c *Functions) GetVertexAttrib(index int, pname Enum) int {
syscall.Syscall(_glGetVertexAttribiv.Addr(), 3, uintptr(index), uintptr(pname), uintptr(unsafe.Pointer(&c.int32s[0]))) syscall.Syscall(_glGetVertexAttribiv.Addr(), 3, uintptr(index), uintptr(pname), uintptr(unsafe.Pointer(&c.int32s[0])))
return int(c.int32s[0]) return int(c.int32s[0])
@@ -524,6 +594,7 @@ func (c *Functions) GetVertexAttribPointer(index int, pname Enum) uintptr {
syscall.Syscall(_glGetVertexAttribPointerv.Addr(), 3, uintptr(index), uintptr(pname), uintptr(unsafe.Pointer(&c.uintptrs[0]))) syscall.Syscall(_glGetVertexAttribPointerv.Addr(), 3, uintptr(index), uintptr(pname), uintptr(unsafe.Pointer(&c.uintptrs[0])))
return c.uintptrs[0] return c.uintptrs[0]
} }
func (c *Functions) InvalidateFramebuffer(target, attachment Enum) { func (c *Functions) InvalidateFramebuffer(target, attachment Enum) {
addr := _glInvalidateFramebuffer.Addr() addr := _glInvalidateFramebuffer.Addr()
if addr == 0 { if addr == 0 {
@@ -532,77 +603,99 @@ func (c *Functions) InvalidateFramebuffer(target, attachment Enum) {
} }
syscall.Syscall(addr, 3, uintptr(target), 1, uintptr(unsafe.Pointer(&attachment))) syscall.Syscall(addr, 3, uintptr(target), 1, uintptr(unsafe.Pointer(&attachment)))
} }
func (f *Functions) IsEnabled(cap Enum) bool { func (f *Functions) IsEnabled(cap Enum) bool {
u, _, _ := syscall.Syscall(_glIsEnabled.Addr(), 1, uintptr(cap), 0, 0) u, _, _ := syscall.Syscall(_glIsEnabled.Addr(), 1, uintptr(cap), 0, 0)
return u == TRUE return u == TRUE
} }
func (c *Functions) LinkProgram(p Program) { func (c *Functions) LinkProgram(p Program) {
syscall.Syscall(_glLinkProgram.Addr(), 1, uintptr(p.V), 0, 0) syscall.Syscall(_glLinkProgram.Addr(), 1, uintptr(p.V), 0, 0)
} }
func (c *Functions) PixelStorei(pname Enum, param int) { func (c *Functions) PixelStorei(pname Enum, param int) {
syscall.Syscall(_glPixelStorei.Addr(), 2, uintptr(pname), uintptr(param), 0) syscall.Syscall(_glPixelStorei.Addr(), 2, uintptr(pname), uintptr(param), 0)
} }
func (f *Functions) MemoryBarrier(barriers Enum) { func (f *Functions) MemoryBarrier(barriers Enum) {
panic("not implemented") panic("not implemented")
} }
func (f *Functions) MapBufferRange(target Enum, offset, length int, access Enum) []byte { func (f *Functions) MapBufferRange(target Enum, offset, length int, access Enum) []byte {
panic("not implemented") panic("not implemented")
} }
func (f *Functions) ReadPixels(x, y, width, height int, format, ty Enum, data []byte) { func (f *Functions) ReadPixels(x, y, width, height int, format, ty Enum, data []byte) {
d0 := &data[0] 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) 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) issue34474KeepAlive(d0)
} }
func (c *Functions) RenderbufferStorage(target, internalformat Enum, width, height int) { 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) syscall.Syscall6(_glRenderbufferStorage.Addr(), 4, uintptr(target), uintptr(internalformat), uintptr(width), uintptr(height), 0, 0)
} }
func (c *Functions) Scissor(x, y, width, height int32) { func (c *Functions) Scissor(x, y, width, height int32) {
syscall.Syscall6(_glScissor.Addr(), 4, uintptr(x), uintptr(y), uintptr(width), uintptr(height), 0, 0) syscall.Syscall6(_glScissor.Addr(), 4, uintptr(x), uintptr(y), uintptr(width), uintptr(height), 0, 0)
} }
func (c *Functions) ShaderSource(s Shader, src string) { func (c *Functions) ShaderSource(s Shader, src string) {
var n uintptr = uintptr(len(src)) var n uintptr = uintptr(len(src))
psrc := &src psrc := &src
syscall.Syscall6(_glShaderSource.Addr(), 4, uintptr(s.V), 1, uintptr(unsafe.Pointer(psrc)), uintptr(unsafe.Pointer(&n)), 0, 0) syscall.Syscall6(_glShaderSource.Addr(), 4, uintptr(s.V), 1, uintptr(unsafe.Pointer(psrc)), uintptr(unsafe.Pointer(&n)), 0, 0)
issue34474KeepAlive(psrc) issue34474KeepAlive(psrc)
} }
func (f *Functions) TexImage2D(target Enum, level int, internalFormat Enum, width int, height int, format Enum, ty Enum) { 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) 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) { 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) 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) { func (c *Functions) TexSubImage2D(target Enum, level int, x, y, width, height int, format, ty Enum, data []byte) {
d0 := &data[0] 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))) 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) issue34474KeepAlive(d0)
} }
func (c *Functions) TexParameteri(target, pname Enum, param int) { func (c *Functions) TexParameteri(target, pname Enum, param int) {
syscall.Syscall(_glTexParameteri.Addr(), 3, uintptr(target), uintptr(pname), uintptr(param)) syscall.Syscall(_glTexParameteri.Addr(), 3, uintptr(target), uintptr(pname), uintptr(param))
} }
func (f *Functions) UniformBlockBinding(p Program, uniformBlockIndex uint, uniformBlockBinding uint) { func (f *Functions) UniformBlockBinding(p Program, uniformBlockIndex uint, uniformBlockBinding uint) {
syscall.Syscall(_glUniformBlockBinding.Addr(), 3, uintptr(p.V), uintptr(uniformBlockIndex), uintptr(uniformBlockBinding)) syscall.Syscall(_glUniformBlockBinding.Addr(), 3, uintptr(p.V), uintptr(uniformBlockIndex), uintptr(uniformBlockBinding))
} }
func (c *Functions) Uniform1f(dst Uniform, v float32) { func (c *Functions) Uniform1f(dst Uniform, v float32) {
syscall.Syscall(_glUniform1f.Addr(), 2, uintptr(dst.V), uintptr(math.Float32bits(v)), 0) syscall.Syscall(_glUniform1f.Addr(), 2, uintptr(dst.V), uintptr(math.Float32bits(v)), 0)
} }
func (c *Functions) Uniform1i(dst Uniform, v int) { func (c *Functions) Uniform1i(dst Uniform, v int) {
syscall.Syscall(_glUniform1i.Addr(), 2, uintptr(dst.V), uintptr(v), 0) syscall.Syscall(_glUniform1i.Addr(), 2, uintptr(dst.V), uintptr(v), 0)
} }
func (c *Functions) Uniform2f(dst Uniform, v0, v1 float32) { 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))) 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) { 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) 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) { 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) 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) { func (c *Functions) UseProgram(p Program) {
syscall.Syscall(_glUseProgram.Addr(), 1, uintptr(p.V), 0, 0) syscall.Syscall(_glUseProgram.Addr(), 1, uintptr(p.V), 0, 0)
} }
func (f *Functions) UnmapBuffer(target Enum) bool { func (f *Functions) UnmapBuffer(target Enum) bool {
panic("not implemented") panic("not implemented")
} }
func (c *Functions) VertexAttribPointer(dst Attrib, size int, ty Enum, normalized bool, stride, offset int) { func (c *Functions) VertexAttribPointer(dst Attrib, size int, ty Enum, normalized bool, stride, offset int) {
var norm uintptr var norm uintptr
if normalized { if normalized {
@@ -610,6 +703,7 @@ func (c *Functions) VertexAttribPointer(dst Attrib, size int, ty Enum, normalize
} }
syscall.Syscall6(_glVertexAttribPointer.Addr(), 6, uintptr(dst), uintptr(size), uintptr(ty), norm, uintptr(stride), uintptr(offset)) 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) { func (c *Functions) Viewport(x, y, width, height int) {
syscall.Syscall6(_glViewport.Addr(), 4, uintptr(x), uintptr(y), uintptr(width), uintptr(height), 0, 0) syscall.Syscall6(_glViewport.Addr(), 4, uintptr(x), uintptr(y), uintptr(width), uintptr(height), 0, 0)
} }
@@ -622,6 +716,6 @@ func cString(s string) []byte {
// issue34474KeepAlive calls runtime.KeepAlive as a // issue34474KeepAlive calls runtime.KeepAlive as a
// workaround for golang.org/issue/34474. // workaround for golang.org/issue/34474.
func issue34474KeepAlive(v interface{}) { func issue34474KeepAlive(v any) {
runtime.KeepAlive(v) runtime.KeepAlive(v)
} }
+5 -5
View File
@@ -18,7 +18,7 @@ type Ops struct {
// data contains the serialized operations. // data contains the serialized operations.
data []byte data []byte
// refs hold external references for operations. // refs hold external references for operations.
refs []interface{} refs []any
// stringRefs provides space for string references, pointers to which will // stringRefs provides space for string references, pointers to which will
// be stored in refs. Storing a string directly in refs would cause a heap // 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 // allocation, to store the string header in an interface value. The backing
@@ -256,7 +256,7 @@ func PopOp(o *Ops, kind StackKind, sid StackID, macroID uint32) {
o.stacks[kind].pop(sid) o.stacks[kind].pop(sid)
} }
func Write1(o *Ops, n int, ref1 interface{}) []byte { func Write1(o *Ops, n int, ref1 any) []byte {
o.data = append(o.data, make([]byte, n)...) o.data = append(o.data, make([]byte, n)...)
o.refs = append(o.refs, ref1) o.refs = append(o.refs, ref1)
return o.data[len(o.data)-n:] return o.data[len(o.data)-n:]
@@ -269,20 +269,20 @@ func Write1String(o *Ops, n int, ref1 string) []byte {
return o.data[len(o.data)-n:] return o.data[len(o.data)-n:]
} }
func Write2(o *Ops, n int, ref1, ref2 interface{}) []byte { func Write2(o *Ops, n int, ref1, ref2 any) []byte {
o.data = append(o.data, make([]byte, n)...) o.data = append(o.data, make([]byte, n)...)
o.refs = append(o.refs, ref1, ref2) o.refs = append(o.refs, ref1, ref2)
return o.data[len(o.data)-n:] return o.data[len(o.data)-n:]
} }
func Write2String(o *Ops, n int, ref1 interface{}, ref2 string) []byte { func Write2String(o *Ops, n int, ref1 any, ref2 string) []byte {
o.data = append(o.data, make([]byte, n)...) o.data = append(o.data, make([]byte, n)...)
o.stringRefs = append(o.stringRefs, ref2) o.stringRefs = append(o.stringRefs, ref2)
o.refs = append(o.refs, ref1, &o.stringRefs[len(o.stringRefs)-1]) o.refs = append(o.refs, ref1, &o.stringRefs[len(o.stringRefs)-1])
return o.data[len(o.data)-n:] return o.data[len(o.data)-n:]
} }
func Write3(o *Ops, n int, ref1, ref2, ref3 interface{}) []byte { func Write3(o *Ops, n int, ref1, ref2, ref3 any) []byte {
o.data = append(o.data, make([]byte, n)...) o.data = append(o.data, make([]byte, n)...)
o.refs = append(o.refs, ref1, ref2, ref3) o.refs = append(o.refs, ref1, ref2, ref3)
return o.data[len(o.data)-n:] return o.data[len(o.data)-n:]
+2 -2
View File
@@ -20,7 +20,7 @@ type Reader struct {
type EncodedOp struct { type EncodedOp struct {
Key Key Key Key
Data []byte Data []byte
Refs []interface{} Refs []any
} }
// Key is a unique key for a given op. // Key is a unique key for a given op.
@@ -175,7 +175,7 @@ func (op *opMacroDef) decode(data []byte) {
op.endpc.refs = bo.Uint32(data[5:]) op.endpc.refs = bo.Uint32(data[5:])
} }
func (m *macroOp) decode(data []byte, refs []interface{}) { func (m *macroOp) decode(data []byte, refs []any) {
if len(data) < TypeCallLen || len(refs) < 1 || OpType(data[0]) != TypeCall { if len(data) < TypeCallLen || len(refs) < 1 || OpType(data[0]) != TypeCall {
panic("invalid op") panic("invalid op")
} }
+21 -12
View File
@@ -87,7 +87,7 @@ func (qs *StrokeQuads) lineTo(pt f32.Point) {
func (qs *StrokeQuads) arc(f1, f2 f32.Point, angle float32) { func (qs *StrokeQuads) arc(f1, f2 f32.Point, angle float32) {
pen := qs.pen() pen := qs.pen()
m, segments := ArcTransform(pen, f1.Add(pen), f2.Add(pen), angle) m, segments := ArcTransform(pen, f1.Add(pen), f2.Add(pen), angle)
for i := 0; i < segments; i++ { for range segments {
p0 := qs.pen() p0 := qs.pen()
p1 := m.Transform(p0) p1 := m.Transform(p0)
p2 := m.Transform(p1) p2 := m.Transform(p1)
@@ -327,13 +327,26 @@ func strokePathNorm(p0, p1, p2 f32.Point, t, d float32) f32.Point {
func rot90CW(p f32.Point) f32.Point { return f32.Pt(+p.Y, -p.X) } func rot90CW(p f32.Point) f32.Point { return f32.Pt(+p.Y, -p.X) }
func normPt(p f32.Point, l float32) f32.Point { func normPt(p f32.Point, l float32) f32.Point {
if (p.X == 0 && p.Y == l) || (p.Y == 0 && p.X == l) { if (p.X == 0 && p.Y == 0) || l == 0 {
return f32.Point{X: p.X, Y: p.Y} 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)) d := math.Hypot(float64(p.X), float64(p.Y))
l64 := float64(l) l64 := float64(l)
if math.Abs(d-l64) < 1e-10 { if math.Abs(d-l64) < 1e-10 {
return f32.Point{} 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) n := float32(l64 / d)
return f32.Point{X: p.X * n, Y: p.Y * n} return f32.Point{X: p.X * n, Y: p.Y * n}
@@ -440,7 +453,6 @@ func flattenQuadBezier(qs StrokeQuads, p0, p1, p2 f32.Point, d, flatness float32
} }
func (qs *StrokeQuads) addLine(p0, ctrl, p1 f32.Point, t, d float32) { func (qs *StrokeQuads) addLine(p0, ctrl, p1 f32.Point, t, d float32) {
switch i := len(*qs); i { switch i := len(*qs); i {
case 0: case 0:
p0 = p0.Add(strokePathNorm(p0, ctrl, p1, 0, d)) p0 = p0.Add(strokePathNorm(p0, ctrl, p1, 0, d))
@@ -473,7 +485,6 @@ func quadInterp(p, q f32.Point, t float32) f32.Point {
// quadBezierSplit returns the pair of triplets (from,ctrl,to) Bézier curve, // quadBezierSplit returns the pair of triplets (from,ctrl,to) Bézier curve,
// split before (resp. after) the provided parametric t value. // 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) { func quadBezierSplit(p0, p1, p2 f32.Point, t float32) (f32.Point, f32.Point, f32.Point, f32.Point, f32.Point, f32.Point) {
var ( var (
b0 = p0 b0 = p0
b1 = quadInterp(p0, p1, t) b1 = quadInterp(p0, p1, t)
@@ -577,12 +588,10 @@ func ArcTransform(p, f1, f2 f32.Point, angle float32) (transform f32.Affine2D, s
} }
} }
var ( θ := angle / float32(segments)
θ = angle / float32(segments) ref := f32.AffineId() // transform from absolute frame to ellipse-based one
ref f32.Affine2D // transform from absolute frame to ellipse-based one rot := f32.AffineId() // rotation matrix for each segment
rot f32.Affine2D // rotation matrix for each segment inv := f32.AffineId() // transform from ellipse-based frame to absolute one
inv f32.Affine2D // transform from ellipse-based frame to absolute one
)
center := f32.Point{ center := f32.Point{
X: 0.5 * (f1.X + f2.X), X: 0.5 * (f1.X + f2.X),
Y: 0.5 * (f1.Y + f2.Y), Y: 0.5 * (f1.Y + f2.Y),
+106 -1
View File
@@ -9,6 +9,111 @@ import (
"gioui.org/internal/f32" "gioui.org/internal/f32"
) )
func TestNormPt(t *testing.T) {
type scenario struct {
l float32
ptIn, ptOut f32.Point
}
scenarios := []scenario{
// l!=0 && X=Y=0
{l: 10, ptIn: f32.Point{X: 0, Y: 0}, ptOut: f32.Point{X: 0, Y: 0}},
{l: -10, ptIn: f32.Point{X: 0, Y: 0}, ptOut: f32.Point{X: 0, Y: 0}},
// l>0 & X
{l: +20, ptIn: f32.Point{X: +30, Y: 0}, ptOut: f32.Point{X: +20, Y: 0}},
{l: +20, ptIn: f32.Point{X: +20, Y: 0}, ptOut: f32.Point{X: +20, Y: 0}},
{l: +20, ptIn: f32.Point{X: +10, Y: 0}, ptOut: f32.Point{X: +20, Y: 0}},
{l: +20, ptIn: f32.Point{X: -10, Y: 0}, ptOut: f32.Point{X: -20, Y: 0}},
{l: +20, ptIn: f32.Point{X: -20, Y: 0}, ptOut: f32.Point{X: -20, Y: 0}},
{l: +20, ptIn: f32.Point{X: -30, Y: 0}, ptOut: f32.Point{X: -20, Y: 0}},
// l<0 & X
{l: -20, ptIn: f32.Point{X: +30, Y: 0}, ptOut: f32.Point{X: -20, Y: 0}},
{l: -20, ptIn: f32.Point{X: +20, Y: 0}, ptOut: f32.Point{X: -20, Y: 0}},
{l: -20, ptIn: f32.Point{X: +10, Y: 0}, ptOut: f32.Point{X: -20, Y: 0}},
{l: -20, ptIn: f32.Point{X: -10, Y: 0}, ptOut: f32.Point{X: +20, Y: 0}},
{l: -20, ptIn: f32.Point{X: -20, Y: 0}, ptOut: f32.Point{X: +20, Y: 0}},
{l: -20, ptIn: f32.Point{X: -30, Y: 0}, ptOut: f32.Point{X: +20, Y: 0}},
// l>0 & Y
{l: +20, ptIn: f32.Point{X: 0, Y: +30}, ptOut: f32.Point{X: 0, Y: +20}},
{l: +20, ptIn: f32.Point{X: 0, Y: +20}, ptOut: f32.Point{X: 0, Y: +20}},
{l: +20, ptIn: f32.Point{X: 0, Y: +10}, ptOut: f32.Point{X: 0, Y: +20}},
{l: +20, ptIn: f32.Point{X: 0, Y: -10}, ptOut: f32.Point{X: 0, Y: -20}},
{l: +20, ptIn: f32.Point{X: 0, Y: -20}, ptOut: f32.Point{X: 0, Y: -20}},
{l: +20, ptIn: f32.Point{X: 0, Y: -30}, ptOut: f32.Point{X: 0, Y: -20}},
// l<0 & Y
{l: -20, ptIn: f32.Point{X: 0, Y: +30}, ptOut: f32.Point{X: 0, Y: -20}},
{l: -20, ptIn: f32.Point{X: 0, Y: +20}, ptOut: f32.Point{X: 0, Y: -20}},
{l: -20, ptIn: f32.Point{X: 0, Y: +10}, ptOut: f32.Point{X: 0, Y: -20}},
{l: -20, ptIn: f32.Point{X: 0, Y: -10}, ptOut: f32.Point{X: 0, Y: +20}},
{l: -20, ptIn: f32.Point{X: 0, Y: -20}, ptOut: f32.Point{X: 0, Y: +20}},
{l: -20, ptIn: f32.Point{X: 0, Y: -30}, ptOut: f32.Point{X: 0, Y: +20}},
// l>0 && X=Y
{l: +20, ptIn: f32.Point{X: +90, Y: +90}, ptOut: f32.Point{X: +14.142137, Y: +14.142137}},
{l: +20, ptIn: f32.Point{X: +30, Y: +30}, ptOut: f32.Point{X: +14.142136, Y: +14.142136}},
{l: +20, ptIn: f32.Point{X: +20, Y: +20}, ptOut: f32.Point{X: +14.142136, Y: +14.142136}},
{l: +20, ptIn: f32.Point{X: +10, Y: +10}, ptOut: f32.Point{X: +14.142136, Y: +14.142136}},
{l: +20, ptIn: f32.Point{X: -10, Y: -10}, ptOut: f32.Point{X: -14.142136, Y: -14.142136}},
{l: +20, ptIn: f32.Point{X: -20, Y: -20}, ptOut: f32.Point{X: -14.142136, Y: -14.142136}},
{l: +20, ptIn: f32.Point{X: -30, Y: -30}, ptOut: f32.Point{X: -14.142136, Y: -14.142136}},
{l: +20, ptIn: f32.Point{X: -90, Y: -90}, ptOut: f32.Point{X: -14.142137, Y: -14.142137}},
// l>0 && X=-Y
{l: +20, ptIn: f32.Point{X: +90, Y: -90}, ptOut: f32.Point{X: +14.142137, Y: -14.142137}},
{l: +20, ptIn: f32.Point{X: +30, Y: -30}, ptOut: f32.Point{X: +14.142136, Y: -14.142136}},
{l: +20, ptIn: f32.Point{X: +20, Y: -20}, ptOut: f32.Point{X: +14.142136, Y: -14.142136}},
{l: +20, ptIn: f32.Point{X: +10, Y: -10}, ptOut: f32.Point{X: +14.142136, Y: -14.142136}},
{l: +20, ptIn: f32.Point{X: -10, Y: +10}, ptOut: f32.Point{X: -14.142136, Y: +14.142136}},
{l: +20, ptIn: f32.Point{X: -20, Y: +20}, ptOut: f32.Point{X: -14.142136, Y: +14.142136}},
{l: +20, ptIn: f32.Point{X: -30, Y: +30}, ptOut: f32.Point{X: -14.142136, Y: +14.142136}},
{l: +20, ptIn: f32.Point{X: -90, Y: +90}, ptOut: f32.Point{X: -14.142137, Y: +14.142137}},
// l<0 && X=Y
{l: -20, ptIn: f32.Point{X: +90, Y: +90}, ptOut: f32.Point{X: -14.142137, Y: -14.142137}},
{l: -20, ptIn: f32.Point{X: +30, Y: +30}, ptOut: f32.Point{X: -14.142136, Y: -14.142136}},
{l: -20, ptIn: f32.Point{X: +20, Y: +20}, ptOut: f32.Point{X: -14.142136, Y: -14.142136}},
{l: -20, ptIn: f32.Point{X: +10, Y: +10}, ptOut: f32.Point{X: -14.142136, Y: -14.142136}},
{l: -20, ptIn: f32.Point{X: -10, Y: -10}, ptOut: f32.Point{X: +14.142136, Y: +14.142136}},
{l: -20, ptIn: f32.Point{X: -20, Y: -20}, ptOut: f32.Point{X: +14.142136, Y: +14.142136}},
{l: -20, ptIn: f32.Point{X: -30, Y: -30}, ptOut: f32.Point{X: +14.142136, Y: +14.142136}},
{l: -20, ptIn: f32.Point{X: -90, Y: -90}, ptOut: f32.Point{X: +14.142137, Y: +14.142137}},
// l<0 && X=-Y
{l: -20, ptIn: f32.Point{X: +90, Y: -90}, ptOut: f32.Point{X: -14.142137, Y: +14.142137}},
{l: -20, ptIn: f32.Point{X: +30, Y: -30}, ptOut: f32.Point{X: -14.142136, Y: +14.142136}},
{l: -20, ptIn: f32.Point{X: +20, Y: -20}, ptOut: f32.Point{X: -14.142136, Y: +14.142136}},
{l: -20, ptIn: f32.Point{X: +10, Y: -10}, ptOut: f32.Point{X: -14.142136, Y: +14.142136}},
{l: -20, ptIn: f32.Point{X: -10, Y: +10}, ptOut: f32.Point{X: +14.142136, Y: -14.142136}},
{l: -20, ptIn: f32.Point{X: -20, Y: +20}, ptOut: f32.Point{X: +14.142136, Y: -14.142136}},
{l: -20, ptIn: f32.Point{X: -30, Y: +30}, ptOut: f32.Point{X: +14.142136, Y: -14.142136}},
{l: -20, ptIn: f32.Point{X: -90, Y: +90}, ptOut: f32.Point{X: +14.142137, Y: -14.142137}},
// l!=0 && Hypot=l
{l: 5, ptIn: f32.Point{X: 3, Y: 4}, ptOut: f32.Point{X: 3, Y: 4}},
{l: 5, ptIn: f32.Point{X: 3, Y: -4}, ptOut: f32.Point{X: 3, Y: -4}},
{l: 5, ptIn: f32.Point{X: -3, Y: -4}, ptOut: f32.Point{X: -3, Y: -4}},
{l: 5, ptIn: f32.Point{X: -3, Y: 4}, ptOut: f32.Point{X: -3, Y: 4}},
{l: -5, ptIn: f32.Point{X: 3, Y: 4}, ptOut: f32.Point{X: -3, Y: -4}},
{l: -5, ptIn: f32.Point{X: 3, Y: -4}, ptOut: f32.Point{X: -3, Y: 4}},
{l: -5, ptIn: f32.Point{X: -3, Y: -4}, ptOut: f32.Point{X: 3, Y: 4}},
{l: -5, ptIn: f32.Point{X: -3, Y: 4}, ptOut: f32.Point{X: 3, Y: -4}},
}
for i, s := range scenarios {
t.Run(strconv.Itoa(i), func(t *testing.T) {
actual := normPt(s.ptIn, s.l)
if actual != s.ptOut {
t.Errorf("in: %v*%v, expected: %v, actual: %v", s.l, s.ptIn, s.ptOut, actual)
}
})
}
}
func BenchmarkSplitCubic(b *testing.B) { func BenchmarkSplitCubic(b *testing.B) {
type scenario struct { type scenario struct {
segments int segments int
@@ -52,7 +157,7 @@ func BenchmarkSplitCubic(b *testing.B) {
from, ctrl0, ctrl1, to := s.from, s.ctrl0, s.ctrl1, s.to from, ctrl0, ctrl1, to := s.from, s.ctrl0, s.ctrl1, s.to
quads := make([]QuadSegment, s.segments) quads := make([]QuadSegment, s.segments)
b.ResetTimer() b.ResetTimer()
for i := 0; i < b.N; i++ { for b.Loop() {
quads = SplitCubic(from, ctrl0, ctrl1, to, quads[:0]) quads = SplitCubic(from, ctrl0, ctrl1, to, quads[:0])
} }
if len(quads) != s.segments { if len(quads) != s.segments {
+1 -1
View File
@@ -385,6 +385,7 @@ static VkResult vkQueuePresentKHR(PFN_vkQueuePresentKHR f, VkQueue queue, const
} }
*/ */
import "C" import "C"
import ( import (
"errors" "errors"
"fmt" "fmt"
@@ -1167,7 +1168,6 @@ func CreateFramebuffer(d Device, rp RenderPass, view ImageView, width, height in
return nilFramebuffer, fmt.Errorf("vulkan: vkCreateFramebuffer: %w", err) return nilFramebuffer, fmt.Errorf("vulkan: vkCreateFramebuffer: %w", err)
} }
return fbo, nil return fbo, nil
} }
func DestroyFramebuffer(d Device, f Framebuffer) { func DestroyFramebuffer(d Device, f Framebuffer) {
+1
View File
@@ -17,6 +17,7 @@ static VkResult vkCreateAndroidSurfaceKHR(PFN_vkCreateAndroidSurfaceKHR f, VkIns
} }
*/ */
import "C" import "C"
import ( import (
"fmt" "fmt"
"unsafe" "unsafe"
+1
View File
@@ -19,6 +19,7 @@ static VkResult vkCreateWaylandSurfaceKHR(PFN_vkCreateWaylandSurfaceKHR f, VkIns
} }
*/ */
import "C" import "C"
import ( import (
"fmt" "fmt"
"unsafe" "unsafe"
+1
View File
@@ -17,6 +17,7 @@ static VkResult vkCreateXlibSurfaceKHR(PFN_vkCreateXlibSurfaceKHR f, VkInstance
} }
*/ */
import "C" import "C"
import ( import (
"fmt" "fmt"
"unsafe" "unsafe"
+1 -1
View File
@@ -10,7 +10,7 @@ import (
// Tag is the stable identifier for an event handler. // Tag is the stable identifier for an event handler.
// For a handler h, the tag is typically &h. // For a handler h, the tag is typically &h.
type Tag interface{} type Tag any
// Event is the marker interface for events. // Event is the marker interface for events.
type Event interface { type Event interface {
+3 -4
View File
@@ -4,6 +4,7 @@ package input
import ( import (
"io" "io"
"slices"
"gioui.org/io/clipboard" "gioui.org/io/clipboard"
"gioui.org/io/event" "gioui.org/io/event"
@@ -60,10 +61,8 @@ func (q *clipboardQueue) ProcessWriteClipboard(req clipboard.WriteCmd) {
} }
func (q *clipboardQueue) ProcessReadClipboard(state clipboardState, tag event.Tag) clipboardState { func (q *clipboardQueue) ProcessReadClipboard(state clipboardState, tag event.Tag) clipboardState {
for _, k := range state.receivers { if slices.Contains(state.receivers, tag) {
if k == tag { return state
return state
}
} }
n := len(state.receivers) n := len(state.receivers)
state.receivers = append(state.receivers[:n:n], tag) state.receivers = append(state.receivers[:n:n], tag)
+1 -1
View File
@@ -52,7 +52,7 @@ func TestQueueProcessReadClipboard(t *testing.T) {
assertClipboardReadCmd(t, r, 1) assertClipboardReadCmd(t, r, 1)
ops.Reset() ops.Reset()
for i := 0; i < 3; i++ { for range 3 {
// No ReadCmd // No ReadCmd
// One receiver must still wait for response // One receiver must still wait for response
+4 -4
View File
@@ -4,6 +4,7 @@ package input
import ( import (
"image" "image"
"slices"
"sort" "sort"
"gioui.org/f32" "gioui.org/f32"
@@ -280,6 +281,7 @@ func (q *keyQueue) Focus(handlers map[event.Tag]*handler, state keyState, focus
return state, nil return state, nil
} }
state.content = EditorState{} state.content = EditorState{}
state.content.Selection.Transform = f32.AffineId()
var evts []taggedEvent var evts []taggedEvent
if state.focus != nil { if state.focus != nil {
evts = append(evts, taggedEvent{tag: state.focus, event: key.FocusEvent{Focus: false}}) evts = append(evts, taggedEvent{tag: state.focus, event: key.FocusEvent{Focus: false}})
@@ -304,10 +306,8 @@ func (s keyState) softKeyboard(show bool) keyState {
} }
func (k *keyFilter) Add(f key.Filter) { func (k *keyFilter) Add(f key.Filter) {
for _, f2 := range *k { if slices.Contains(*k, f) {
if f == f2 { return
return
}
} }
*k = append(*k, f) *k = append(*k, f)
} }
+26 -36
View File
@@ -5,6 +5,7 @@ package input
import ( import (
"image" "image"
"io" "io"
"slices"
"gioui.org/f32" "gioui.org/f32"
f32internal "gioui.org/internal/f32" f32internal "gioui.org/internal/f32"
@@ -143,7 +144,9 @@ const (
) )
func (c *pointerCollector) resetState() { func (c *pointerCollector) resetState() {
c.state = collectState{} c.state = collectState{
t: f32.AffineId(),
}
c.nodeStack = c.nodeStack[:0] c.nodeStack = c.nodeStack[:0]
// Pop every node except the root. // Pop every node except the root.
if len(c.q.hitTree) > 0 { if len(c.q.hitTree) > 0 {
@@ -257,6 +260,11 @@ func (q *pointerQueue) grab(state pointerState, req pointer.GrabCmd) (pointerSta
if !p.pressed || p.id != req.ID { if !p.pressed || p.id != req.ID {
continue continue
} }
// Verify that the grabber is among the handlers.
found := slices.Contains(p.handlers, req.Tag)
if !found {
continue
}
// Drop other handlers that lost their grab. // Drop other handlers that lost their grab.
for i := len(p.handlers) - 1; i >= 0; i-- { for i := len(p.handlers) - 1; i >= 0; i-- {
if tag := p.handlers[i]; tag != req.Tag { if tag := p.handlers[i]; tag != req.Tag {
@@ -282,17 +290,13 @@ func (c *pointerCollector) inputOp(tag event.Tag, state *pointerHandler) {
func (p *pointerFilter) Add(f event.Filter) { func (p *pointerFilter) Add(f event.Filter) {
switch f := f.(type) { switch f := f.(type) {
case transfer.SourceFilter: case transfer.SourceFilter:
for _, m := range p.sourceMimes { if slices.Contains(p.sourceMimes, f.Type) {
if m == f.Type { return
return
}
} }
p.sourceMimes = append(p.sourceMimes, f.Type) p.sourceMimes = append(p.sourceMimes, f.Type)
case transfer.TargetFilter: case transfer.TargetFilter:
for _, m := range p.targetMimes { if slices.Contains(p.targetMimes, f.Type) {
if m == f.Type { return
return
}
} }
p.targetMimes = append(p.targetMimes, f.Type) p.targetMimes = append(p.targetMimes, f.Type)
case pointer.Filter: case pointer.Filter:
@@ -309,16 +313,12 @@ func (p *pointerFilter) Matches(e event.Event) bool {
case transfer.CancelEvent, transfer.InitiateEvent: case transfer.CancelEvent, transfer.InitiateEvent:
return len(p.sourceMimes) > 0 || len(p.targetMimes) > 0 return len(p.sourceMimes) > 0 || len(p.targetMimes) > 0
case transfer.RequestEvent: case transfer.RequestEvent:
for _, t := range p.sourceMimes { if slices.Contains(p.sourceMimes, e.Type) {
if t == e.Type { return true
return true
}
} }
case transfer.DataEvent: case transfer.DataEvent:
for _, t := range p.targetMimes { if slices.Contains(p.targetMimes, e.Type) {
if t == e.Type { return true
return true
}
} }
} }
return false return false
@@ -413,7 +413,7 @@ func (q *pointerQueue) offerData(handlers map[event.Tag]*handler, state pointerS
}, },
}}) }})
} }
state.pointers = append([]pointerInfo{}, state.pointers...) state.pointers = slices.Clone(state.pointers)
state.pointers[i], evts = q.deliverTransferCancelEvent(handlers, p, evts) state.pointers[i], evts = q.deliverTransferCancelEvent(handlers, p, evts)
break break
} }
@@ -605,7 +605,7 @@ func (q *pointerQueue) reset() {
for k, ids := range q.semantic.contentIDs { for k, ids := range q.semantic.contentIDs {
for i := len(ids) - 1; i >= 0; i-- { for i := len(ids) - 1; i >= 0; i-- {
if !ids[i].used { if !ids[i].used {
ids = append(ids[:i], ids[i+1:]...) ids = slices.Delete(ids, i, i+1)
} else { } else {
ids[i].used = false ids[i].used = false
} }
@@ -636,7 +636,7 @@ func (q *pointerQueue) Frame(handlers map[event.Tag]*handler, state pointerState
changed := false changed := false
p, evts, state.cursor, changed = q.deliverEnterLeaveEvents(handlers, state.cursor, p, evts, p.last) p, evts, state.cursor, changed = q.deliverEnterLeaveEvents(handlers, state.cursor, p, evts, p.last)
if changed { if changed {
state.pointers = append([]pointerInfo{}, state.pointers...) state.pointers = slices.Clone(state.pointers)
state.pointers[i] = p state.pointers[i] = p
} }
} }
@@ -772,19 +772,17 @@ func (q *pointerQueue) Push(handlers map[event.Tag]*handler, state pointerState,
if !p.pressed && len(p.entered) == 0 { if !p.pressed && len(p.entered) == 0 {
// No longer need to track pointer. // No longer need to track pointer.
state.pointers = append(state.pointers[:pidx:pidx], state.pointers[pidx+1:]...) state.pointers = slices.Concat(state.pointers[:pidx:pidx], state.pointers[pidx+1:])
} else { } else {
state.pointers = append([]pointerInfo{}, state.pointers...) state.pointers = slices.Clone(state.pointers)
state.pointers[pidx] = p state.pointers[pidx] = p
} }
return state, evts return state, evts
} }
func (q *pointerQueue) deliverEvent(handlers map[event.Tag]*handler, p pointerInfo, evts []taggedEvent, e pointer.Event) []taggedEvent { func (q *pointerQueue) deliverEvent(handlers map[event.Tag]*handler, p pointerInfo, evts []taggedEvent, e pointer.Event) []taggedEvent {
foremost := true
if p.pressed && len(p.handlers) == 1 { if p.pressed && len(p.handlers) == 1 {
e.Priority = pointer.Grabbed e.Priority = pointer.Grabbed
foremost = false
} }
scroll := e.Scroll scroll := e.Scroll
for _, k := range p.handlers { for _, k := range p.handlers {
@@ -803,10 +801,6 @@ func (q *pointerQueue) deliverEvent(handlers map[event.Tag]*handler, p pointerIn
scroll, e.Scroll = f.clampScroll(scroll) scroll, e.Scroll = f.clampScroll(scroll)
} }
e := e e := e
if foremost {
foremost = false
e.Priority = pointer.Foremost
}
e.Position = q.invTransform(h.pointer.areaPlusOne-1, e.Position) e.Position = q.invTransform(h.pointer.areaPlusOne-1, e.Position)
evts = append(evts, taggedEvent{event: e, tag: k}) evts = append(evts, taggedEvent{event: e, tag: k})
} }
@@ -970,10 +964,8 @@ func searchTag(tags []event.Tag, tag event.Tag) (int, bool) {
// addHandler adds tag to the slice if not present. // addHandler adds tag to the slice if not present.
func addHandler(tags []event.Tag, tag event.Tag) []event.Tag { func addHandler(tags []event.Tag, tag event.Tag) []event.Tag {
for _, t := range tags { if slices.Contains(tags, tag) {
if t == tag { return tags
return tags
}
} }
return append(tags, tag) return append(tags, tag)
} }
@@ -981,10 +973,8 @@ func addHandler(tags []event.Tag, tag event.Tag) []event.Tag {
// firstMimeMatch returns the first type match between src and tgt. // firstMimeMatch returns the first type match between src and tgt.
func firstMimeMatch(src, tgt *pointerFilter) (first string, matched bool) { func firstMimeMatch(src, tgt *pointerFilter) (first string, matched bool) {
for _, m1 := range tgt.targetMimes { for _, m1 := range tgt.targetMimes {
for _, m2 := range src.sourceMimes { if slices.Contains(src.sourceMimes, m1) {
if m1 == m2 { return m1, true
return m1, true
}
} }
} }
return "", false return "", false
+55 -14
View File
@@ -97,6 +97,39 @@ func TestPointerDragNegative(t *testing.T) {
assertEventPointerTypeSequence(t, events(&r, -1, f), pointer.Enter, pointer.Press, pointer.Leave, pointer.Drag) assertEventPointerTypeSequence(t, events(&r, -1, f), pointer.Enter, pointer.Press, pointer.Leave, pointer.Drag)
} }
func TestIgnoredGrab(t *testing.T) {
handler1 := new(int)
handler2 := new(int)
var ops op.Ops
filter := func(t event.Tag) event.Filter {
return pointer.Filter{Target: t, Kinds: pointer.Press | pointer.Release | pointer.Cancel}
}
event.Op(&ops, handler1)
event.Op(&ops, handler2)
var r Router
assertEventPointerTypeSequence(t, events(&r, -1, filter(handler1)), pointer.Cancel)
assertEventPointerTypeSequence(t, events(&r, -1, filter(handler2)), pointer.Cancel)
r.Frame(&ops)
r.Queue(
pointer.Event{
Kind: pointer.Press,
Position: f32.Pt(50, 50),
},
pointer.Event{
Kind: pointer.Release,
Position: f32.Pt(50, 50),
},
)
assertEventPointerTypeSequence(t, events(&r, 1, filter(handler1)), pointer.Press)
assertEventPointerTypeSequence(t, events(&r, 1, filter(handler2)), pointer.Press)
r.Source().Execute(pointer.GrabCmd{Tag: handler1})
r.Source().Execute(pointer.GrabCmd{Tag: handler2})
assertEventPointerTypeSequence(t, events(&r, 1, filter(handler1)), pointer.Release)
assertEventPointerTypeSequence(t, events(&r, 1, filter(handler2)), pointer.Cancel)
}
func TestPointerGrab(t *testing.T) { func TestPointerGrab(t *testing.T) {
handler1 := new(int) handler1 := new(int)
handler2 := new(int) handler2 := new(int)
@@ -367,9 +400,9 @@ func TestPointerPriority(t *testing.T) {
assertEventPointerTypeSequence(t, hev1, pointer.Scroll, pointer.Scroll) assertEventPointerTypeSequence(t, hev1, pointer.Scroll, pointer.Scroll)
assertEventPointerTypeSequence(t, hev2, pointer.Scroll) assertEventPointerTypeSequence(t, hev2, pointer.Scroll)
assertEventPointerTypeSequence(t, hev3, pointer.Scroll) assertEventPointerTypeSequence(t, hev3, pointer.Scroll)
assertEventPriorities(t, hev1, pointer.Shared, pointer.Foremost) assertEventPriorities(t, hev1, pointer.Shared, pointer.Shared)
assertEventPriorities(t, hev2, pointer.Foremost) assertEventPriorities(t, hev2, pointer.Shared)
assertEventPriorities(t, hev3, pointer.Foremost) assertEventPriorities(t, hev3, pointer.Shared)
assertScrollEvent(t, hev1[0], f32.Pt(30, 0)) assertScrollEvent(t, hev1[0], f32.Pt(30, 0))
assertScrollEvent(t, hev2[0], f32.Pt(20, 0)) assertScrollEvent(t, hev2[0], f32.Pt(20, 0))
assertScrollEvent(t, hev1[1], f32.Pt(50, 0)) assertScrollEvent(t, hev1[1], f32.Pt(50, 0))
@@ -678,26 +711,31 @@ func TestCursor(t *testing.T) {
cursors []pointer.Cursor cursors []pointer.Cursor
want pointer.Cursor want pointer.Cursor
}{ }{
{label: "no movement", {
label: "no movement",
cursors: []pointer.Cursor{pointer.CursorPointer}, cursors: []pointer.Cursor{pointer.CursorPointer},
want: pointer.CursorDefault, want: pointer.CursorDefault,
}, },
{label: "move inside", {
label: "move inside",
cursors: []pointer.Cursor{pointer.CursorPointer}, cursors: []pointer.Cursor{pointer.CursorPointer},
events: _at(50, 50), events: _at(50, 50),
want: pointer.CursorPointer, want: pointer.CursorPointer,
}, },
{label: "move outside", {
label: "move outside",
cursors: []pointer.Cursor{pointer.CursorPointer}, cursors: []pointer.Cursor{pointer.CursorPointer},
events: _at(200, 200), events: _at(200, 200),
want: pointer.CursorDefault, want: pointer.CursorDefault,
}, },
{label: "move back inside", {
label: "move back inside",
cursors: []pointer.Cursor{pointer.CursorPointer}, cursors: []pointer.Cursor{pointer.CursorPointer},
events: _at(50, 50), events: _at(50, 50),
want: pointer.CursorPointer, want: pointer.CursorPointer,
}, },
{label: "send key events while inside", {
label: "send key events while inside",
cursors: []pointer.Cursor{pointer.CursorPointer}, cursors: []pointer.Cursor{pointer.CursorPointer},
events: []event.Event{ events: []event.Event{
key.Event{Name: "A", State: key.Press}, key.Event{Name: "A", State: key.Press},
@@ -705,7 +743,8 @@ func TestCursor(t *testing.T) {
}, },
want: pointer.CursorPointer, want: pointer.CursorPointer,
}, },
{label: "send key events while outside", {
label: "send key events while outside",
cursors: []pointer.Cursor{pointer.CursorPointer}, cursors: []pointer.Cursor{pointer.CursorPointer},
events: append( events: append(
_at(200, 200), _at(200, 200),
@@ -714,7 +753,8 @@ func TestCursor(t *testing.T) {
), ),
want: pointer.CursorDefault, want: pointer.CursorDefault,
}, },
{label: "add new input on top while inside", {
label: "add new input on top while inside",
cursors: []pointer.Cursor{pointer.CursorPointer, pointer.CursorCrosshair}, cursors: []pointer.Cursor{pointer.CursorPointer, pointer.CursorCrosshair},
events: append( events: append(
_at(50, 50), _at(50, 50),
@@ -725,7 +765,8 @@ func TestCursor(t *testing.T) {
), ),
want: pointer.CursorCrosshair, want: pointer.CursorCrosshair,
}, },
{label: "remove input on top while inside", {
label: "remove input on top while inside",
cursors: []pointer.Cursor{pointer.CursorPointer}, cursors: []pointer.Cursor{pointer.CursorPointer},
events: append( events: append(
_at(50, 50), _at(50, 50),
@@ -1110,7 +1151,7 @@ func TestPartialEvent(t *testing.T) {
key.FocusEvent{}, pointer.Event{Kind: pointer.Press, Source: pointer.Mouse, Priority: pointer.Shared}) key.FocusEvent{}, pointer.Event{Kind: pointer.Press, Source: pointer.Mouse, Priority: pointer.Shared})
r.Source().Execute(key.FocusCmd{Tag: 1}) r.Source().Execute(key.FocusCmd{Tag: 1})
assertEventSequence(t, events(&r, -1, pointer.Filter{Target: 2, Kinds: pointer.Press}), assertEventSequence(t, events(&r, -1, pointer.Filter{Target: 2, Kinds: pointer.Press}),
pointer.Event{Kind: pointer.Press, Source: pointer.Mouse, Priority: pointer.Foremost}) pointer.Event{Kind: pointer.Press, Source: pointer.Mouse, Priority: pointer.Shared})
} }
// offer satisfies io.ReadCloser for use in data transfers. // offer satisfies io.ReadCloser for use in data transfers.
@@ -1256,7 +1297,7 @@ func BenchmarkRouterAdd(b *testing.B) {
handlerCount := i handlerCount := i
b.Run(fmt.Sprintf("%d-handlers", i), func(b *testing.B) { b.Run(fmt.Sprintf("%d-handlers", i), func(b *testing.B) {
handlers := make([]event.Tag, handlerCount) handlers := make([]event.Tag, handlerCount)
for i := 0; i < handlerCount; i++ { for i := range handlerCount {
h := new(int) h := new(int)
*h = i *h = i
handlers[i] = h handlers[i] = h
@@ -1278,7 +1319,7 @@ func BenchmarkRouterAdd(b *testing.B) {
r.Frame(&ops) r.Frame(&ops)
b.ReportAllocs() b.ReportAllocs()
b.ResetTimer() b.ResetTimer()
for i := 0; i < b.N; i++ { for b.Loop() {
r.Queue( r.Queue(
pointer.Event{ pointer.Event{
Kind: pointer.Move, Kind: pointer.Move,
+29 -17
View File
@@ -5,6 +5,7 @@ package input
import ( import (
"image" "image"
"io" "io"
"slices"
"strings" "strings"
"time" "time"
@@ -62,7 +63,8 @@ type Router struct {
// Source implements the interface between a Router and user interface widgets. // Source implements the interface between a Router and user interface widgets.
// The zero-value Source is disabled. // The zero-value Source is disabled.
type Source struct { type Source struct {
r *Router r *Router
disabled bool
} }
// Command represents a request such as moving the focus, or initiating a clipboard read. // Command represents a request such as moving the focus, or initiating a clipboard read.
@@ -171,30 +173,38 @@ func (q *Router) Source() Source {
// Execute a command. // Execute a command.
func (s Source) Execute(c Command) { func (s Source) Execute(c Command) {
if !s.enabled() { if !s.Enabled() {
return return
} }
s.r.execute(c) s.r.execute(c)
} }
// enabled reports whether the source is enabled. Only enabled // Disabled returns a copy of this source that don't deliver any events.
// Sources deliver events and respond to commands. func (s Source) Disabled() Source {
func (s Source) enabled() bool { s2 := s
return s.r != nil s2.disabled = true
return s2
}
// Enabled reports whether the source is enabled. Only enabled
// Sources deliver events.
func (s Source) Enabled() bool {
return s.r != nil && !s.disabled
} }
// Focused reports whether tag is focused, according to the most recent // Focused reports whether tag is focused, according to the most recent
// [key.FocusEvent] delivered. // [key.FocusEvent] delivered.
func (s Source) Focused(tag event.Tag) bool { func (s Source) Focused(tag event.Tag) bool {
if !s.enabled() { if !s.Enabled() {
return false return false
} }
return s.r.state().keyState.focus == tag return s.r.state().keyState.focus == tag
} }
// Event returns the next event that matches at least one of filters. // Event returns the next event that matches at least one of filters.
// If the source is disabled, no events will be reported.
func (s Source) Event(filters ...event.Filter) (event.Event, bool) { func (s Source) Event(filters ...event.Filter) (event.Event, bool) {
if !s.enabled() { if !s.Enabled() {
return nil, false return nil, false
} }
return s.r.Event(filters...) return s.r.Event(filters...)
@@ -293,7 +303,7 @@ func (q *Router) Event(filters ...event.Filter) (event.Event, bool) {
} }
} }
if match { if match {
change.events = append(change.events[:j], change.events[j+1:]...) change.events = slices.Delete(change.events, j, j+1)
// Fast forward state to last matched. // Fast forward state to last matched.
q.collapseState(i) q.collapseState(i)
return evt.event, true return evt.event, true
@@ -618,11 +628,11 @@ func (q *Router) RevealFocus(viewport image.Rectangle) {
viewport = q.pointer.queue.ClipFor(area, viewport) viewport = q.pointer.queue.ClipFor(area, viewport)
topleft := bounds.Min.Sub(viewport.Min) topleft := bounds.Min.Sub(viewport.Min)
topleft = max(topleft, bounds.Max.Sub(viewport.Max)) topleft = maxPoint(topleft, bounds.Max.Sub(viewport.Max))
topleft = min(image.Pt(0, 0), topleft) topleft = minPoint(image.Pt(0, 0), topleft)
bottomright := bounds.Max.Sub(viewport.Max) bottomright := bounds.Max.Sub(viewport.Max)
bottomright = min(bottomright, bounds.Min.Sub(viewport.Min)) bottomright = minPoint(bottomright, bounds.Min.Sub(viewport.Min))
bottomright = max(image.Pt(0, 0), bottomright) bottomright = maxPoint(image.Pt(0, 0), bottomright)
s := topleft s := topleft
if s.X == 0 { if s.X == 0 {
s.X = bottomright.X s.X = bottomright.X
@@ -649,7 +659,7 @@ func (q *Router) ScrollFocus(dist image.Point) {
})) }))
} }
func max(p1, p2 image.Point) image.Point { func maxPoint(p1, p2 image.Point) image.Point {
m := p1 m := p1
if p2.X > m.X { if p2.X > m.X {
m.X = p2.X m.X = p2.X
@@ -660,7 +670,7 @@ func max(p1, p2 image.Point) image.Point {
return m return m
} }
func min(p1, p2 image.Point) image.Point { func minPoint(p1, p2 image.Point) image.Point {
m := p1 m := p1
if p2.X < m.X { if p2.X < m.X {
m.X = p2.X m.X = p2.X
@@ -769,13 +779,15 @@ func (q *Router) collect() {
pc.Reset() pc.Reset()
kq := &q.key.queue kq := &q.key.queue
q.key.queue.Reset() q.key.queue.Reset()
var t f32.Affine2D t := f32.AffineId()
for encOp, ok := q.reader.Decode(); ok; encOp, ok = q.reader.Decode() { for encOp, ok := q.reader.Decode(); ok; encOp, ok = q.reader.Decode() {
switch ops.OpType(encOp.Data[0]) { switch ops.OpType(encOp.Data[0]) {
case ops.TypeSave: case ops.TypeSave:
id := ops.DecodeSave(encOp.Data) id := ops.DecodeSave(encOp.Data)
if extra := id - len(q.savedTrans) + 1; extra > 0 { if extra := id - len(q.savedTrans) + 1; extra > 0 {
q.savedTrans = append(q.savedTrans, make([]f32.Affine2D, extra)...) for range extra {
q.savedTrans = append(q.savedTrans, f32.AffineId())
}
} }
q.savedTrans[id] = t q.savedTrans[id] = t
case ops.TypeLoad: case ops.TypeLoad:
+1 -1
View File
@@ -15,7 +15,7 @@ func TestNoFilterAllocs(t *testing.T) {
s := r.Source() s := r.Source()
b.ReportAllocs() b.ReportAllocs()
b.ResetTimer() b.ResetTimer()
for i := 0; i < b.N; i++ { for b.Loop() {
s.Event(pointer.Filter{}) s.Event(pointer.Filter{})
} }
}) })
+1 -1
View File
@@ -125,7 +125,7 @@ func verifyTree(t *testing.T, parent SemanticID, n SemanticNode) {
} }
func printTree(indent int, n SemanticNode) { func printTree(indent int, n SemanticNode) {
for i := 0; i < indent; i++ { for range indent {
fmt.Print("\t") fmt.Print("\t")
} }
fmt.Printf("%d: %+v\n", n.ID, n.Desc) fmt.Printf("%d: %+v\n", n.ID, n.Desc)
+14 -8
View File
@@ -19,7 +19,7 @@ type Event struct {
Source Source Source Source
// PointerID is the id for the pointer and can be used // PointerID is the id for the pointer and can be used
// to track a particular pointer from Press to // to track a particular pointer from Press to
// Release or Cancel. // Release.
PointerID ID PointerID ID
// Priority is the priority of the receiving handler // Priority is the priority of the receiving handler
// for this event. // for this event.
@@ -43,8 +43,7 @@ type Event struct {
// PassOp sets the pass-through mode. InputOps added while the pass-through // PassOp sets the pass-through mode. InputOps added while the pass-through
// mode is set don't block events to siblings. // mode is set don't block events to siblings.
type PassOp struct { type PassOp struct{}
}
// PassStack represents a PassOp on the pass stack. // PassStack represents a PassOp on the pass stack.
type PassStack struct { type PassStack struct {
@@ -207,9 +206,6 @@ const (
// Shared priority is for handlers that // Shared priority is for handlers that
// are part of a matching set larger than 1. // are part of a matching set larger than 1.
Shared Priority = iota Shared Priority = iota
// Foremost priority is like Shared, but the
// handler is the foremost of the matching set.
Foremost
// Grabbed is used for matching sets of size 1. // Grabbed is used for matching sets of size 1.
Grabbed Grabbed
) )
@@ -223,6 +219,12 @@ const (
ButtonSecondary ButtonSecondary
// ButtonTertiary is the tertiary button, usually the middle button. // ButtonTertiary is the tertiary button, usually the middle button.
ButtonTertiary ButtonTertiary
// ButtonQuaternary is the fourth button, usually used for browser
// navigation (backward)
ButtonQuaternary
// ButtonQuinary is the fifth button, usually used for browser
// navigation (forward)
ButtonQuinary
) )
func (s ScrollRange) Union(s2 ScrollRange) ScrollRange { func (s ScrollRange) Union(s2 ScrollRange) ScrollRange {
@@ -295,8 +297,6 @@ func (p Priority) String() string {
switch p { switch p {
case Shared: case Shared:
return "Shared" return "Shared"
case Foremost:
return "Foremost"
case Grabbed: case Grabbed:
return "Grabbed" return "Grabbed"
default: default:
@@ -332,6 +332,12 @@ func (b Buttons) String() string {
if b.Contain(ButtonTertiary) { if b.Contain(ButtonTertiary) {
strs = append(strs, "ButtonTertiary") strs = append(strs, "ButtonTertiary")
} }
if b.Contain(ButtonQuaternary) {
strs = append(strs, "ButtonQuaternary")
}
if b.Contain(ButtonQuinary) {
strs = append(strs, "ButtonQuinary")
}
return strings.Join(strs, "|") return strings.Join(strs, "|")
} }
+5 -16
View File
@@ -5,7 +5,6 @@ package layout
import ( import (
"time" "time"
"gioui.org/io/event"
"gioui.org/io/input" "gioui.org/io/input"
"gioui.org/io/system" "gioui.org/io/system"
"gioui.org/op" "gioui.org/op"
@@ -29,7 +28,10 @@ type Context struct {
// Interested users must look up and populate these values manually. // Interested users must look up and populate these values manually.
Locale system.Locale Locale system.Locale
disabled bool // Values is a map of program global data associated with the context.
// It is not for use by widgets.
Values map[string]any
input.Source input.Source
*op.Ops *op.Ops
} }
@@ -44,21 +46,8 @@ func (c Context) Sp(v unit.Sp) int {
return c.Metric.Sp(v) return c.Metric.Sp(v)
} }
func (c Context) Event(filters ...event.Filter) (event.Event, bool) {
if c.disabled {
return nil, false
}
return c.Source.Event(filters...)
}
// Enabled reports whether this context is enabled. Disabled contexts
// don't report events.
func (c Context) Enabled() bool {
return !c.disabled
}
// Disabled returns a copy of this context that don't deliver any events. // Disabled returns a copy of this context that don't deliver any events.
func (c Context) Disabled() Context { func (c Context) Disabled() Context {
c.disabled = true c.Source = c.Source.Disabled()
return c return c
} }
+24 -14
View File
@@ -30,10 +30,6 @@ type FlexChild struct {
weight float32 weight float32
widget Widget widget Widget
// Scratch space.
call op.CallOp
dims Dimensions
} }
// Spacing determine the spacing mode for a Flex. // Spacing determine the spacing mode for a Flex.
@@ -88,6 +84,20 @@ func (f Flex) Layout(gtx Context, children ...FlexChild) Dimensions {
remaining := mainMax remaining := mainMax
var totalWeight float32 var totalWeight float32
cgtx := gtx cgtx := gtx
// Note: previously the scratch space was inside FlexChild.
// child.call.Add(gtx.Ops) confused the go escape analysis and caused the
// entired children slice to be allocated on the heap, including all widgets
// in it. This produced a lot of object allocations. Now the scratch space
// is separate from children, and for cases len(children) <= 32, we will
// allocate the scratch space on the stack. For cases len(children) > 32,
// only the scratch space gets allocated from the heap, during append.
type scratchSpace struct {
call op.CallOp
dims Dimensions
}
var scratchArray [32]scratchSpace
scratch := scratchArray[:0]
scratch = append(scratch, make([]scratchSpace, len(children))...)
// Lay out Rigid children. // Lay out Rigid children.
for i, child := range children { for i, child := range children {
if child.flex { if child.flex {
@@ -104,8 +114,8 @@ func (f Flex) Layout(gtx Context, children ...FlexChild) Dimensions {
if remaining < 0 { if remaining < 0 {
remaining = 0 remaining = 0
} }
children[i].call = c scratch[i].call = c
children[i].dims = dims scratch[i].dims = dims
} }
if w := f.WeightSum; w != 0 { if w := f.WeightSum; w != 0 {
totalWeight = w totalWeight = w
@@ -139,16 +149,16 @@ func (f Flex) Layout(gtx Context, children ...FlexChild) Dimensions {
if remaining < 0 { if remaining < 0 {
remaining = 0 remaining = 0
} }
children[i].call = c scratch[i].call = c
children[i].dims = dims scratch[i].dims = dims
} }
maxCross := crossMin maxCross := crossMin
var maxBaseline int var maxBaseline int
for _, child := range children { for _, scratchChild := range scratch {
if c := f.Axis.Convert(child.dims.Size).Y; c > maxCross { if c := f.Axis.Convert(scratchChild.dims.Size).Y; c > maxCross {
maxCross = c maxCross = c
} }
if b := child.dims.Size.Y - child.dims.Baseline; b > maxBaseline { if b := scratchChild.dims.Size.Y - scratchChild.dims.Baseline; b > maxBaseline {
maxBaseline = b maxBaseline = b
} }
} }
@@ -169,8 +179,8 @@ func (f Flex) Layout(gtx Context, children ...FlexChild) Dimensions {
mainSize += space / (len(children) * 2) mainSize += space / (len(children) * 2)
} }
} }
for i, child := range children { for i, scratchChild := range scratch {
dims := child.dims dims := scratchChild.dims
b := dims.Size.Y - dims.Baseline b := dims.Size.Y - dims.Baseline
var cross int var cross int
switch f.Alignment { switch f.Alignment {
@@ -185,7 +195,7 @@ func (f Flex) Layout(gtx Context, children ...FlexChild) Dimensions {
} }
pt := f.Axis.Convert(image.Pt(mainSize, cross)) pt := f.Axis.Convert(image.Pt(mainSize, cross))
trans := op.Offset(pt).Push(gtx.Ops) trans := op.Offset(pt).Push(gtx.Ops)
child.call.Add(gtx.Ops) scratchChild.call.Add(gtx.Ops)
trans.Pop() trans.Pop()
mainSize += f.Axis.Convert(dims.Size).X mainSize += f.Axis.Convert(dims.Size).X
if i < len(children)-1 { if i < len(children)-1 {
+11 -6
View File
@@ -29,6 +29,8 @@ type List struct {
ScrollToEnd bool ScrollToEnd bool
// Alignment is the cross axis alignment of list elements. // Alignment is the cross axis alignment of list elements.
Alignment Alignment Alignment Alignment
// ScrollAnyAxis allows any scroll axis to scroll the list, not just the main axis.
ScrollAnyAxis bool
cs Constraints cs Constraints
scroll gesture.Scroll scroll gesture.Scroll
@@ -159,12 +161,19 @@ func (l *List) update(gtx Context) {
max = 0 max = 0
} }
} }
xrange := pointer.ScrollRange{Min: min, Max: max} xrange := pointer.ScrollRange{Min: min, Max: max}
yrange := pointer.ScrollRange{} yrange := pointer.ScrollRange{}
if l.Axis == Vertical {
axis := gesture.Axis(l.Axis)
if l.ScrollAnyAxis {
axis = gesture.Both
yrange = xrange
} else if l.Axis == Vertical {
xrange, yrange = yrange, xrange xrange, yrange = yrange, xrange
} }
d := l.scroll.Update(gtx.Metric, gtx.Source, gtx.Now, gesture.Axis(l.Axis), xrange, yrange) d := l.scroll.Update(gtx.Metric, gtx.Source, gtx.Now, axis, xrange, yrange)
l.scrollDelta = d l.scrollDelta = d
l.Position.Offset += d l.Position.Offset += d
} }
@@ -308,10 +317,6 @@ func (l *List) layout(ops *op.Ops, macro op.MacroOp) Dimensions {
cross = (maxCross - sz.Y) / 2 cross = (maxCross - sz.Y) / 2
} }
childSize := sz.X childSize := sz.X
min := pos
if min < 0 {
min = 0
}
pt := l.Axis.Convert(image.Pt(pos, cross)) pt := l.Axis.Convert(image.Pt(pos, cross))
trans := op.Offset(pt).Push(ops) trans := op.Offset(pt).Push(ops)
child.call.Add(ops) child.call.Add(ops)
+12 -6
View File
@@ -88,7 +88,8 @@ func TestListPosition(t *testing.T) {
{label: "1 visible 0 hidden", num: 1, count: 1, last: 10}, {label: "1 visible 0 hidden", num: 1, count: 1, last: 10},
{label: "2 visible 0 hidden", num: 2, count: 2}, {label: "2 visible 0 hidden", num: 2, count: 2},
{label: "2 visible 1 hidden", num: 3, count: 2}, {label: "2 visible 1 hidden", num: 3, count: 2},
{label: "3 visible 0 hidden small scroll", num: 3, count: 3, offset: 5, last: -5, {
label: "3 visible 0 hidden small scroll", num: 3, count: 3, offset: 5, last: -5,
scroll: _s( scroll: _s(
pointer.Event{ pointer.Event{
Source: pointer.Mouse, Source: pointer.Mouse,
@@ -107,8 +108,10 @@ func TestListPosition(t *testing.T) {
Kind: pointer.Release, Kind: pointer.Release,
Position: f32.Pt(5, 0), Position: f32.Pt(5, 0),
}, },
)}, ),
{label: "3 visible 0 hidden small scroll 2", num: 3, count: 3, offset: 3, last: -7, },
{
label: "3 visible 0 hidden small scroll 2", num: 3, count: 3, offset: 3, last: -7,
scroll: _s( scroll: _s(
pointer.Event{ pointer.Event{
Source: pointer.Mouse, Source: pointer.Mouse,
@@ -127,8 +130,10 @@ func TestListPosition(t *testing.T) {
Kind: pointer.Release, Kind: pointer.Release,
Position: f32.Pt(5, 0), Position: f32.Pt(5, 0),
}, },
)}, ),
{label: "2 visible 1 hidden large scroll", num: 3, count: 2, first: 1, },
{
label: "2 visible 1 hidden large scroll", num: 3, count: 2, first: 1,
scroll: _s( scroll: _s(
pointer.Event{ pointer.Event{
Source: pointer.Mouse, Source: pointer.Mouse,
@@ -147,7 +152,8 @@ func TestListPosition(t *testing.T) {
Kind: pointer.Release, Kind: pointer.Release,
Position: f32.Pt(15, 0), Position: f32.Pt(15, 0),
}, },
)}, ),
},
} { } {
t.Run(tc.label, func(t *testing.T) { t.Run(tc.label, func(t *testing.T) {
gtx.Ops.Reset() gtx.Ops.Reset()
+22 -12
View File
@@ -20,10 +20,6 @@ type Stack struct {
type StackChild struct { type StackChild struct {
expanded bool expanded bool
widget Widget widget Widget
// Scratch space.
call op.CallOp
dims Dimensions
} }
// Stacked returns a Stack child that is laid out with no minimum // Stacked returns a Stack child that is laid out with no minimum
@@ -52,6 +48,20 @@ func (s Stack) Layout(gtx Context, children ...StackChild) Dimensions {
// First lay out Stacked children. // First lay out Stacked children.
cgtx := gtx cgtx := gtx
cgtx.Constraints.Min = image.Point{} cgtx.Constraints.Min = image.Point{}
// Note: previously the scratch space was inside StackChild.
// child.call.Add(gtx.Ops) confused the go escape analysis and caused the
// entired children slice to be allocated on the heap, including all widgets
// in it. This produced a lot of object allocations. Now the scratch space
// is separate from children, and for cases len(children) <= 32, we will
// allocate the scratch space on the stack. For cases len(children) > 32,
// only the scratch space gets allocated from the heap, during append.
type scratchSpace struct {
call op.CallOp
dims Dimensions
}
var scratchArray [32]scratchSpace
scratch := scratchArray[:0]
scratch = append(scratch, make([]scratchSpace, len(children))...)
for i, w := range children { for i, w := range children {
if w.expanded { if w.expanded {
continue continue
@@ -65,8 +75,8 @@ func (s Stack) Layout(gtx Context, children ...StackChild) Dimensions {
if h := dims.Size.Y; h > maxSZ.Y { if h := dims.Size.Y; h > maxSZ.Y {
maxSZ.Y = h maxSZ.Y = h
} }
children[i].call = call scratch[i].call = call
children[i].dims = dims scratch[i].dims = dims
} }
// Then lay out Expanded children. // Then lay out Expanded children.
for i, w := range children { for i, w := range children {
@@ -83,14 +93,14 @@ func (s Stack) Layout(gtx Context, children ...StackChild) Dimensions {
if h := dims.Size.Y; h > maxSZ.Y { if h := dims.Size.Y; h > maxSZ.Y {
maxSZ.Y = h maxSZ.Y = h
} }
children[i].call = call scratch[i].call = call
children[i].dims = dims scratch[i].dims = dims
} }
maxSZ = gtx.Constraints.Constrain(maxSZ) maxSZ = gtx.Constraints.Constrain(maxSZ)
var baseline int var baseline int
for _, ch := range children { for _, scratchChild := range scratch {
sz := ch.dims.Size sz := scratchChild.dims.Size
var p image.Point var p image.Point
switch s.Alignment { switch s.Alignment {
case N, S, Center: case N, S, Center:
@@ -105,10 +115,10 @@ func (s Stack) Layout(gtx Context, children ...StackChild) Dimensions {
p.Y = maxSZ.Y - sz.Y p.Y = maxSZ.Y - sz.Y
} }
trans := op.Offset(p).Push(gtx.Ops) trans := op.Offset(p).Push(gtx.Ops)
ch.call.Add(gtx.Ops) scratchChild.call.Add(gtx.Ops)
trans.Pop() trans.Pop()
if baseline == 0 { if baseline == 0 {
if b := ch.dims.Baseline; b != 0 { if b := scratchChild.dims.Baseline; b != 0 {
baseline = b + maxSZ.Y - sz.Y - p.Y baseline = b + maxSZ.Y - sz.Y - p.Y
} }
} }
+2 -4
View File
@@ -17,9 +17,8 @@ func BenchmarkStack(b *testing.B) {
}, },
} }
b.ReportAllocs() b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ { for b.Loop() {
gtx.Ops.Reset() gtx.Ops.Reset()
Stack{}.Layout(gtx, Stack{}.Layout(gtx,
@@ -41,9 +40,8 @@ func BenchmarkBackground(b *testing.B) {
}, },
} }
b.ReportAllocs() b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ { for b.Loop() {
gtx.Ops.Reset() gtx.Ops.Reset()
Background{}.Layout(gtx, Background{}.Layout(gtx,
+7 -4
View File
@@ -214,8 +214,9 @@ func (p *Path) LineTo(to f32.Point) {
bo := binary.LittleEndian bo := binary.LittleEndian
bo.PutUint32(data[0:], uint32(p.contour)) bo.PutUint32(data[0:], uint32(p.contour))
p.cmd(data[4:], scene.Line(p.pen, to)) p.cmd(data[4:], scene.Line(p.pen, to))
p.pen = to p.expand(p.pen)
p.expand(to) p.expand(to)
p.pen = to
} }
func (p *Path) cmd(data []byte, c scene.Command) { func (p *Path) cmd(data []byte, c scene.Command) {
@@ -263,9 +264,10 @@ func (p *Path) QuadTo(ctrl, to f32.Point) {
bo := binary.LittleEndian bo := binary.LittleEndian
bo.PutUint32(data[0:], uint32(p.contour)) bo.PutUint32(data[0:], uint32(p.contour))
p.cmd(data[4:], scene.Quad(p.pen, ctrl, to)) p.cmd(data[4:], scene.Quad(p.pen, ctrl, to))
p.pen = to p.expand(p.pen)
p.expand(ctrl) p.expand(ctrl)
p.expand(to) p.expand(to)
p.pen = to
} }
// ArcTo adds an elliptical arc to the path. The implied ellipse is defined // ArcTo adds an elliptical arc to the path. The implied ellipse is defined
@@ -275,7 +277,7 @@ func (p *Path) QuadTo(ctrl, to f32.Point) {
// negative clockwise. // negative clockwise.
func (p *Path) ArcTo(f1, f2 f32.Point, angle float32) { func (p *Path) ArcTo(f1, f2 f32.Point, angle float32) {
m, segments := stroke.ArcTransform(p.pen, f1, f2, angle) m, segments := stroke.ArcTransform(p.pen, f1, f2, angle)
for i := 0; i < segments; i++ { for range segments {
p0 := p.pen p0 := p.pen
p1 := m.Transform(p0) p1 := m.Transform(p0)
p2 := m.Transform(p1) p2 := m.Transform(p1)
@@ -307,10 +309,11 @@ func (p *Path) CubeTo(ctrl0, ctrl1, to f32.Point) {
bo := binary.LittleEndian bo := binary.LittleEndian
bo.PutUint32(data[0:], uint32(p.contour)) bo.PutUint32(data[0:], uint32(p.contour))
p.cmd(data[4:], scene.Cubic(p.pen, ctrl0, ctrl1, to)) p.cmd(data[4:], scene.Cubic(p.pen, ctrl0, ctrl1, to))
p.pen = to p.expand(p.pen)
p.expand(ctrl0) p.expand(ctrl0)
p.expand(ctrl1) p.expand(ctrl1)
p.expand(to) p.expand(to)
p.pen = to
} }
// Close closes the path contour. // Close closes the path contour.
+106
View File
@@ -0,0 +1,106 @@
// SPDX-License-Identifier: Unlicense OR MIT
package clip
import (
"gioui.org/f32"
"gioui.org/op"
"math"
"testing"
)
func TestPath_MoveTo_LineTo(t *testing.T) {
var ops op.Ops
p := Path{}
p.Begin(&ops)
startPoint := f32.Pt(32, 32)
endPoint := f32.Pt(64, 64)
p.MoveTo(startPoint)
p.LineTo(endPoint)
pathSpec := p.End()
minPoint := f32.Pt(32, 32)
maxPoint := f32.Pt(64, 64)
if pathSpec.bounds.Min == pathSpec.bounds.Max {
t.Errorf("zero path")
}
if pathSpec.bounds.Min != minPoint.Round() {
t.Errorf("pathSpec.bounds.Min = %v, want %v", pathSpec.bounds.Min, minPoint)
}
if pathSpec.bounds.Max != maxPoint.Round() {
t.Errorf("pathSpec.bounds.Max = %v, want %v", pathSpec.bounds.Max, maxPoint)
}
}
func TestPath_MoveTo_QuadTo(t *testing.T) {
var ops op.Ops
p := Path{}
p.Begin(&ops)
startPoint := f32.Pt(32, 32)
midPoint := f32.Pt(60, 60)
p.MoveTo(startPoint)
p.QuadTo(midPoint.Sub(f32.Pt(-4, 0)), midPoint.Sub(f32.Pt(0, -4)))
pathSpec := p.End()
minPoint := f32.Pt(32, 32)
maxPoint := f32.Pt(64, 64)
if pathSpec.bounds.Min == pathSpec.bounds.Max {
t.Errorf("zero path")
}
if pathSpec.bounds.Min != minPoint.Round() {
t.Errorf("pathSpec.bounds.Min = %v, want %v", pathSpec.bounds.Min, minPoint)
}
if pathSpec.bounds.Max != maxPoint.Round() {
t.Errorf("pathSpec.bounds.Max = %v, want %v", pathSpec.bounds.Max, maxPoint)
}
}
func TestPath_MoveTo_ArcTo(t *testing.T) {
// We need a tolerance here because of rounding errors.
tolerance := f32.Pt(1, 1)
var ops op.Ops
p := Path{}
p.Begin(&ops)
arcStartPoint := f32.Pt(48, 32)
arcCenterPoint := f32.Pt(48, 48)
p.MoveTo(arcStartPoint)
p.ArcTo(arcCenterPoint, arcCenterPoint, math.Pi*2)
pathSpec := p.End()
minPoint := f32.Pt(32, 32).Sub(tolerance).Round()
maxPoint := f32.Pt(64, 64).Add(tolerance).Round()
if pathSpec.bounds.Min == pathSpec.bounds.Max {
t.Errorf("zero path")
}
if pathSpec.bounds.Min.X < minPoint.X || pathSpec.bounds.Min.Y < minPoint.Y {
t.Errorf("pathSpec.bounds.Min = %v, want %v", pathSpec.bounds.Min, minPoint)
}
if pathSpec.bounds.Max.X > maxPoint.X || pathSpec.bounds.Max.Y > maxPoint.Y {
t.Errorf("pathSpec.bounds.Max = %v, want %v", pathSpec.bounds.Max, maxPoint)
}
}
func TestPath_MoveTo_CubeTo(t *testing.T) {
var ops op.Ops
p := Path{}
p.Begin(&ops)
startPoint := f32.Pt(32, 32)
midPoint := f32.Pt(48, 48)
endPoint := f32.Pt(64, 64)
p.MoveTo(startPoint)
p.CubeTo(midPoint.Sub(f32.Pt(-4, 0)), midPoint.Sub(f32.Pt(0, -4)), endPoint)
pathSpec := p.End()
minPoint := f32.Pt(32, 32)
maxPoint := f32.Pt(64, 64)
if pathSpec.bounds.Min == pathSpec.bounds.Max {
t.Errorf("zero path")
}
if pathSpec.bounds.Min != minPoint.Round() {
t.Errorf("pathSpec.bounds.Min = %v, want %v", pathSpec.bounds.Min, minPoint)
}
if pathSpec.bounds.Max != maxPoint.Round() {
t.Errorf("pathSpec.bounds.Max = %v, want %v", pathSpec.bounds.Max, maxPoint)
}
}
+1 -1
View File
@@ -183,7 +183,7 @@ func (c CallOp) Add(o *Ops) {
// Offset converts an offset to a TransformOp. // Offset converts an offset to a TransformOp.
func Offset(off image.Point) TransformOp { func Offset(off image.Point) TransformOp {
offf := f32.Pt(float32(off.X), float32(off.Y)) offf := f32.Pt(float32(off.X), float32(off.Y))
return Affine(f32.Affine2D{}.Offset(offf)) return Affine(f32.AffineId().Offset(offf))
} }
// Affine creates a TransformOp representing the transformation a. // Affine creates a TransformOp representing the transformation a.
+2 -3
View File
@@ -35,7 +35,7 @@ type ImageOp struct {
// handle is a key to uniquely identify this ImageOp // handle is a key to uniquely identify this ImageOp
// in a map of cached textures. // in a map of cached textures.
handle interface{} handle any
} }
// ColorOp sets the brush to a constant color. // ColorOp sets the brush to a constant color.
@@ -53,8 +53,7 @@ type LinearGradientOp struct {
} }
// PaintOp fills the current clip area with the current brush. // PaintOp fills the current clip area with the current brush.
type PaintOp struct { type PaintOp struct{}
}
// OpacityStack represents an opacity applied to all painting operations // OpacityStack represents an opacity applied to all painting operations
// until Pop is called. // until Pop is called.
+1 -2
View File
@@ -1,9 +1,8 @@
package text package text
import ( import (
"slices"
"testing" "testing"
"golang.org/x/exp/slices"
) )
func TestParser(t *testing.T) { func TestParser(t *testing.T) {
+5 -12
View File
@@ -9,6 +9,7 @@ import (
"io" "io"
"log" "log"
"os" "os"
"slices"
"github.com/go-text/typesetting/di" "github.com/go-text/typesetting/di"
"github.com/go-text/typesetting/font" "github.com/go-text/typesetting/font"
@@ -16,7 +17,6 @@ import (
"github.com/go-text/typesetting/fontscan" "github.com/go-text/typesetting/fontscan"
"github.com/go-text/typesetting/language" "github.com/go-text/typesetting/language"
"github.com/go-text/typesetting/shaping" "github.com/go-text/typesetting/shaping"
"golang.org/x/exp/slices"
"golang.org/x/image/math/fixed" "golang.org/x/image/math/fixed"
"golang.org/x/text/unicode/bidi" "golang.org/x/text/unicode/bidi"
@@ -56,13 +56,6 @@ func (l *document) reset() {
l.unreadRuneCount = 0 l.unreadRuneCount = 0
} }
func max(a, b int) int {
if a > b {
return a
}
return b
}
// A line contains the measurements of a line of text. // A line contains the measurements of a line of text.
type line struct { type line struct {
// runs contains sequences of shaped glyphs with common attributes. The order // runs contains sequences of shaped glyphs with common attributes. The order
@@ -356,7 +349,7 @@ func (s *shaperImpl) splitBidi(input shaping.Input) []shaping.Input {
if err != nil { if err != nil {
return []shaping.Input{input} return []shaping.Input{input}
} }
for i := 0; i < out.NumRuns(); i++ { for i := range out.NumRuns() {
currentInput := input currentInput := input
run := out.Run(i) run := out.Run(i)
dir := run.Direction() dir := run.Direction()
@@ -685,7 +678,7 @@ func (s *shaperImpl) Shape(pathOps *op.Ops, gs []Glyph) clip.PathSpec {
nargs = 3 nargs = 3
} }
var args [3]f32.Point var args [3]f32.Point
for i := 0; i < nargs; i++ { for i := range nargs {
a := f32.Point{ a := f32.Point{
X: fseg.Args[i].X * scaleFactor, X: fseg.Args[i].X * scaleFactor,
Y: -fseg.Args[i].Y * scaleFactor, Y: -fseg.Args[i].Y * scaleFactor,
@@ -767,7 +760,7 @@ func (s *shaperImpl) Bitmaps(ops *op.Ops, gs []Glyph) op.CallOp {
imgOp = bitmapData.img imgOp = bitmapData.img
imgSize = bitmapData.size imgSize = bitmapData.size
} }
off := op.Affine(f32.Affine2D{}.Offset(f32.Point{ off := op.Affine(f32.AffineId().Offset(f32.Point{
X: fixedToFloat((g.X - x) - g.Offset.X), X: fixedToFloat((g.X - x) - g.Offset.X),
Y: fixedToFloat(g.Offset.Y + g.Bounds.Min.Y), Y: fixedToFloat(g.Offset.Y + g.Bounds.Min.Y),
})).Push(ops) })).Push(ops)
@@ -783,7 +776,7 @@ func (s *shaperImpl) Bitmaps(ops *op.Ops, gs []Glyph) op.CallOp {
Y: g.Bounds.Max.Y.Round(), Y: g.Bounds.Max.Y.Round(),
}, },
}.Size() }.Size()
aff := op.Affine(f32.Affine2D{}.Scale(f32.Point{}, f32.Point{ aff := op.Affine(f32.AffineId().Scale(f32.Point{}, f32.Point{
X: float32(glyphSize.X) / float32(imgSize.X), X: float32(glyphSize.X) / float32(imgSize.X),
Y: float32(glyphSize.Y) / float32(imgSize.Y), Y: float32(glyphSize.Y) / float32(imgSize.Y),
})).Push(ops) })).Push(ops)
+3 -4
View File
@@ -3,13 +3,13 @@ package text
import ( import (
"fmt" "fmt"
"math" "math"
"slices"
"strconv" "strconv"
"testing" "testing"
nsareg "eliasnaur.com/font/noto/sans/arabic/regular" nsareg "eliasnaur.com/font/noto/sans/arabic/regular"
"github.com/go-text/typesetting/font" "github.com/go-text/typesetting/font"
"github.com/go-text/typesetting/shaping" "github.com/go-text/typesetting/shaping"
"golang.org/x/exp/slices"
"golang.org/x/image/font/gofont/goregular" "golang.org/x/image/font/gofont/goregular"
"golang.org/x/image/math/fixed" "golang.org/x/image/math/fixed"
@@ -126,7 +126,8 @@ func TestShapingAlignWidth(t *testing.T) {
}, },
} { } {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
lines := shaper.LayoutString(Parameters{PxPerEm: ppem, lines := shaper.LayoutString(Parameters{
PxPerEm: ppem,
MinWidth: tc.minWidth, MinWidth: tc.minWidth,
MaxWidth: tc.maxWidth, MaxWidth: tc.maxWidth,
Locale: english, Locale: english,
@@ -174,7 +175,6 @@ func TestNewlineSynthesis(t *testing.T) {
}, },
} { } {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
doc := shaper.LayoutRunes(Parameters{ doc := shaper.LayoutRunes(Parameters{
PxPerEm: ppem, PxPerEm: ppem,
MaxWidth: 200, MaxWidth: 200,
@@ -207,7 +207,6 @@ func TestNewlineSynthesis(t *testing.T) {
} }
}) })
} }
} }
// simpleGlyph returns a simple square glyph with the provided cluster // simpleGlyph returns a simple square glyph with the provided cluster
+2 -2
View File
@@ -35,10 +35,10 @@ func TestPathLRU(t *testing.T) {
} }
func testLRU(t *testing.T, put func(i int), get func(i int) bool) { func testLRU(t *testing.T, put func(i int), get func(i int) bool) {
for i := 0; i < maxSize; i++ { for i := range maxSize {
put(i) put(i)
} }
for i := 0; i < maxSize; i++ { for i := range maxSize {
if !get(i) { if !get(i) {
t.Fatalf("key %d was evicted", i) t.Fatalf("key %d was evicted", i)
} }
+1 -1
View File
@@ -544,7 +544,7 @@ func (l *Shaper) NextGlyph() (_ Glyph, ok bool) {
// of a valid cursor position they can use for "after" such a newline, // of a valid cursor position they can use for "after" such a newline,
// taking text alignment into account. // taking text alignment into account.
l.pararagraphStart.X = l.txt.alignment.Align(line.direction, 0, l.txt.alignWidth) l.pararagraphStart.X = l.txt.alignment.Align(line.direction, 0, l.txt.alignWidth)
l.pararagraphStart.Y = glyph.Y + int32((glyph.Ascent + glyph.Descent).Ceil()) l.pararagraphStart.Y = glyph.Y + int32(line.lineHeight.Round())
} }
} }
return glyph, true return glyph, true
+1 -1
View File
@@ -2,6 +2,7 @@ package text
import ( import (
"fmt" "fmt"
"slices"
"strings" "strings"
"testing" "testing"
@@ -10,7 +11,6 @@ import (
"gioui.org/font/gofont" "gioui.org/font/gofont"
"gioui.org/font/opentype" "gioui.org/font/opentype"
"gioui.org/io/system" "gioui.org/io/system"
"golang.org/x/exp/slices"
"golang.org/x/image/font/gofont/goregular" "golang.org/x/image/font/gofont/goregular"
"golang.org/x/image/math/fixed" "golang.org/x/image/math/fixed"
) )
-4
View File
@@ -9,7 +9,6 @@ import (
"gioui.org/gesture" "gioui.org/gesture"
"gioui.org/io/event" "gioui.org/io/event"
"gioui.org/io/key" "gioui.org/io/key"
"gioui.org/io/pointer"
"gioui.org/io/semantic" "gioui.org/io/semantic"
"gioui.org/layout" "gioui.org/layout"
"gioui.org/op" "gioui.org/op"
@@ -141,9 +140,6 @@ func (b *Clickable) update(t event.Tag, gtx layout.Context) (Click, bool) {
} }
} }
case gesture.KindPress: case gesture.KindPress:
if e.Source == pointer.Mouse {
gtx.Execute(key.FocusCmd{Tag: t})
}
b.history = append(b.history, Press{ b.history = append(b.history, Press{
Position: e.Position, Position: e.Position,
Start: gtx.Now, Start: gtx.Now,
+2 -16
View File
@@ -929,7 +929,7 @@ func (e *Editor) replace(start, end int, s string, addHistory bool) int {
if addHistory { if addHistory {
deleted := make([]rune, 0, replaceSize) deleted := make([]rune, 0, replaceSize)
readPos := e.text.ByteOffset(start) readPos := e.text.ByteOffset(start)
for i := 0; i < replaceSize; i++ { for range replaceSize {
ru, s, _ := e.text.ReadRuneAt(int64(readPos)) ru, s, _ := e.text.ReadRuneAt(int64(readPos))
readPos += int64(s) readPos += int64(s)
deleted = append(deleted, ru) deleted = append(deleted, ru)
@@ -1021,7 +1021,7 @@ func (e *Editor) deleteWord(distance int) (deletedRunes int) {
return r return r
} }
runes := 1 runes := 1
for ii := 0; ii < words; ii++ { for range words {
r := next(runes) r := next(runes)
wantSpace := unicode.IsSpace(r) wantSpace := unicode.IsSpace(r)
for r := next(runes); unicode.IsSpace(r) == wantSpace && !atEnd(runes); r = next(runes) { for r := next(runes); unicode.IsSpace(r) == wantSpace && !atEnd(runes); r = next(runes) {
@@ -1093,20 +1093,6 @@ func (e *Editor) Regions(start, end int, regions []Region) []Region {
return e.text.Regions(start, end, regions) return e.text.Regions(start, end, regions)
} }
func max(a, b int) int {
if a > b {
return a
}
return b
}
func min(a, b int) int {
if a < b {
return a
}
return b
}
func abs(n int) int { func abs(n int) int {
if n < 0 { if n < 0 {
return -n return -n
+2 -2
View File
@@ -992,7 +992,7 @@ func TestEditorSelectShortcuts(t *testing.T) {
tFont := font.Font{} tFont := font.Font{}
tFontSize := unit.Sp(10) tFontSize := unit.Sp(10)
tShaper := text.NewShaper(text.NoSystemFonts(), text.WithCollection(gofont.Collection())) tShaper := text.NewShaper(text.NoSystemFonts(), text.WithCollection(gofont.Collection()))
var tEditor = &Editor{ tEditor := &Editor{
SingleLine: false, SingleLine: false,
ReadOnly: true, ReadOnly: true,
} }
@@ -1255,7 +1255,7 @@ func TestNoFilterAllocs(t *testing.T) {
} }
b.ReportAllocs() b.ReportAllocs()
b.ResetTimer() b.ResetTimer()
for i := 0; i < b.N; i++ { for b.Loop() {
e.Update(gtx) e.Update(gtx)
} }
}) })
+3 -3
View File
@@ -39,7 +39,7 @@ func (fit Fit) scale(cs layout.Constraints, pos layout.Direction, dims layout.Di
offset := pos.Position(widgetSize, dims.Size) offset := pos.Position(widgetSize, dims.Size)
dims.Baseline += offset.Y dims.Baseline += offset.Y
return dims, f32.Affine2D{}.Offset(layout.FPt(offset)) return dims, f32.AffineId().Offset(layout.FPt(offset))
} }
scale := f32.Point{ scale := f32.Point{
@@ -73,7 +73,7 @@ func (fit Fit) scale(cs layout.Constraints, pos layout.Direction, dims layout.Di
offset := pos.Position(widgetSize, dims.Size) offset := pos.Position(widgetSize, dims.Size)
dims.Baseline += offset.Y dims.Baseline += offset.Y
return dims, f32.Affine2D{}.Offset(layout.FPt(offset)) return dims, f32.AffineId().Offset(layout.FPt(offset))
} }
case Fill: case Fill:
} }
@@ -85,7 +85,7 @@ func (fit Fit) scale(cs layout.Constraints, pos layout.Direction, dims layout.Di
dims.Baseline = int(float32(dims.Baseline) * scale.Y) dims.Baseline = int(float32(dims.Baseline) * scale.Y)
offset := pos.Position(scaledSize, dims.Size) offset := pos.Position(scaledSize, dims.Size)
trans := f32.Affine2D{}. trans := f32.AffineId().
Scale(f32.Point{}, scale). Scale(f32.Point{}, scale).
Offset(layout.FPt(offset)) Offset(layout.FPt(offset))
+10 -5
View File
@@ -31,7 +31,8 @@ func TestFit(t *testing.T) {
Dims: image.Point{50, 200}, Dims: image.Point{50, 200},
Scale: f32.Point{X: 1, Y: 1}, Scale: f32.Point{X: 1, Y: 1},
Result: image.Point{X: 50, Y: 100}, Result: image.Point{X: 50, Y: 100},
}}, },
},
Contain: { Contain: {
{ {
Dims: image.Point{50, 25}, Dims: image.Point{50, 25},
@@ -41,7 +42,8 @@ func TestFit(t *testing.T) {
Dims: image.Point{50, 200}, Dims: image.Point{50, 200},
Scale: f32.Point{X: 0.5, Y: 0.5}, Scale: f32.Point{X: 0.5, Y: 0.5},
Result: image.Point{X: 25, Y: 100}, Result: image.Point{X: 25, Y: 100},
}}, },
},
Cover: { Cover: {
{ {
Dims: image.Point{50, 25}, Dims: image.Point{50, 25},
@@ -51,7 +53,8 @@ func TestFit(t *testing.T) {
Dims: image.Point{50, 200}, Dims: image.Point{50, 200},
Scale: f32.Point{X: 2, Y: 2}, Scale: f32.Point{X: 2, Y: 2},
Result: image.Point{X: 100, Y: 100}, Result: image.Point{X: 100, Y: 100},
}}, },
},
ScaleDown: { ScaleDown: {
{ {
Dims: image.Point{50, 25}, Dims: image.Point{50, 25},
@@ -61,7 +64,8 @@ func TestFit(t *testing.T) {
Dims: image.Point{50, 200}, Dims: image.Point{50, 200},
Scale: f32.Point{X: 0.5, Y: 0.5}, Scale: f32.Point{X: 0.5, Y: 0.5},
Result: image.Point{X: 25, Y: 100}, Result: image.Point{X: 25, Y: 100},
}}, },
},
Fill: { Fill: {
{ {
Dims: image.Point{50, 25}, Dims: image.Point{50, 25},
@@ -71,7 +75,8 @@ func TestFit(t *testing.T) {
Dims: image.Point{50, 200}, Dims: image.Point{50, 200},
Scale: f32.Point{X: 2, Y: 0.5}, Scale: f32.Point{X: 2, Y: 0.5},
Result: image.Point{X: 100, Y: 100}, Result: image.Point{X: 100, Y: 100},
}}, },
},
} }
for fit, tests := range fittests { for fit, tests := range fittests {
+1 -1
View File
@@ -44,7 +44,7 @@ func (im Image) Layout(gtx layout.Context) layout.Dimensions {
defer clip.Rect{Max: dims.Size}.Push(gtx.Ops).Pop() defer clip.Rect{Max: dims.Size}.Push(gtx.Ops).Pop()
pixelScale := scale * gtx.Metric.PxPerDp pixelScale := scale * gtx.Metric.PxPerDp
trans = trans.Mul(f32.Affine2D{}.Scale(f32.Point{}, f32.Pt(pixelScale, pixelScale))) trans = trans.Mul(f32.AffineId().Scale(f32.Point{}, f32.Pt(pixelScale, pixelScale)))
defer op.Affine(trans).Push(gtx.Ops).Pop() defer op.Affine(trans).Push(gtx.Ops).Pop()
im.Src.Add(gtx.Ops) im.Src.Add(gtx.Ops)
+22 -17
View File
@@ -73,10 +73,11 @@ func makeAccountingTestText(str string, fontSize, lineWidth int) (txt []text.Gly
ltrFace, _ := opentype.Parse(goregular.TTF) ltrFace, _ := opentype.Parse(goregular.TTF)
rtlFace, _ := opentype.Parse(nsareg.TTF) rtlFace, _ := opentype.Parse(nsareg.TTF)
shaper := text.NewShaper(text.NoSystemFonts(), text.WithCollection([]font.FontFace{{ shaper := text.NewShaper(text.NoSystemFonts(), text.WithCollection([]font.FontFace{
Font: font.Font{Typeface: "LTR"}, {
Face: ltrFace, Font: font.Font{Typeface: "LTR"},
}, Face: ltrFace,
},
{ {
Font: font.Font{Typeface: "RTL"}, Font: font.Font{Typeface: "RTL"},
Face: rtlFace, Face: rtlFace,
@@ -99,10 +100,11 @@ func getGlyphs(fontSize, minWidth, lineWidth int, align text.Alignment, str stri
ltrFace, _ := opentype.Parse(goregular.TTF) ltrFace, _ := opentype.Parse(goregular.TTF)
rtlFace, _ := opentype.Parse(nsareg.TTF) rtlFace, _ := opentype.Parse(nsareg.TTF)
shaper := text.NewShaper(text.NoSystemFonts(), text.WithCollection([]font.FontFace{{ shaper := text.NewShaper(text.NoSystemFonts(), text.WithCollection([]font.FontFace{
Font: font.Font{Typeface: "LTR"}, {
Face: ltrFace, Font: font.Font{Typeface: "LTR"},
}, Face: ltrFace,
},
{ {
Font: font.Font{Typeface: "RTL"}, Font: font.Font{Typeface: "RTL"},
Face: rtlFace, Face: rtlFace,
@@ -245,7 +247,7 @@ func TestIndexPositionWhitespace(t *testing.T) {
if len(gi.positions) != len(tc.expected) { if len(gi.positions) != len(tc.expected) {
t.Errorf("expected %d positions, got %d", len(tc.expected), len(gi.positions)) t.Errorf("expected %d positions, got %d", len(tc.expected), len(gi.positions))
} }
for i := 0; i < min(len(gi.positions), len(tc.expected)); i++ { for i := range min(len(gi.positions), len(tc.expected)) {
actual := gi.positions[i] actual := gi.positions[i]
expected := tc.expected[i] expected := tc.expected[i]
if actual != expected { if actual != expected {
@@ -258,7 +260,6 @@ func TestIndexPositionWhitespace(t *testing.T) {
} }
}) })
} }
} }
// TestIndexPositionBidi tests whether the index correct generates cursor positions for // TestIndexPositionBidi tests whether the index correct generates cursor positions for
@@ -312,7 +313,7 @@ func TestIndexPositionBidi(t *testing.T) {
lastLine := 0 lastLine := 0
lastCol := -1 lastCol := -1
lastY := 0 lastY := 0
for i := 0; i < min(len(gi.positions), len(tc.expectedXs)); i++ { for i := range min(len(gi.positions), len(tc.expectedXs)) {
actualX := gi.positions[i].x actualX := gi.positions[i].x
expectedX := tc.expectedXs[i] expectedX := tc.expectedXs[i]
if actualX != expectedX { if actualX != expectedX {
@@ -538,7 +539,7 @@ func TestIndexPositionLines(t *testing.T) {
if len(gi.lines) != len(tc.expectedLines) { if len(gi.lines) != len(tc.expectedLines) {
t.Errorf("expected %d lines, got %d", len(tc.expectedLines), len(gi.lines)) t.Errorf("expected %d lines, got %d", len(tc.expectedLines), len(gi.lines))
} }
for i := 0; i < min(len(gi.lines), len(tc.expectedLines)); i++ { for i := range min(len(gi.lines), len(tc.expectedLines)) {
actual := gi.lines[i] actual := gi.lines[i]
expected := tc.expectedLines[i] expected := tc.expectedLines[i]
if actual != expected { if actual != expected {
@@ -623,7 +624,7 @@ func TestIndexPositionRunes(t *testing.T) {
if len(gi.positions) != len(tc.expected) { if len(gi.positions) != len(tc.expected) {
t.Errorf("expected %d positions, got %d", len(tc.expected), len(gi.positions)) t.Errorf("expected %d positions, got %d", len(tc.expected), len(gi.positions))
} }
for i := 0; i < min(len(gi.positions), len(tc.expected)); i++ { for i := range min(len(gi.positions), len(tc.expected)) {
actual := gi.positions[i] actual := gi.positions[i]
expected := tc.expected[i] expected := tc.expected[i]
if expected.runes != actual.runes { if expected.runes != actual.runes {
@@ -646,6 +647,7 @@ func TestIndexPositionRunes(t *testing.T) {
}) })
} }
} }
func printPositions(t *testing.T, positions []combinedPos) { func printPositions(t *testing.T, positions []combinedPos) {
t.Helper() t.Helper()
for i, p := range positions { for i, p := range positions {
@@ -720,7 +722,7 @@ func TestGraphemeReaderNext(t *testing.T) {
if len(asRunes) != len(runes) { if len(asRunes) != len(runes) {
t.Errorf("expected %d runes, got %d", len(asRunes), len(runes)) t.Errorf("expected %d runes, got %d", len(asRunes), len(runes))
} }
for i := 0; i < max(len(asRunes), len(runes)); i++ { for i := range max(len(asRunes), len(runes)) {
if i < min(len(asRunes), len(runes)) { if i < min(len(asRunes), len(runes)) {
if runes[i] != asRunes[i] { if runes[i] != asRunes[i] {
t.Errorf("expected runes[%d]=%d, got %d", i, asRunes[i], runes[i]) t.Errorf("expected runes[%d]=%d, got %d", i, asRunes[i], runes[i])
@@ -734,6 +736,7 @@ func TestGraphemeReaderNext(t *testing.T) {
}) })
} }
} }
func TestGraphemeReaderGraphemes(t *testing.T) { func TestGraphemeReaderGraphemes(t *testing.T) {
latinDoc := bytes.NewReader([]byte(latinDocument)) latinDoc := bytes.NewReader([]byte(latinDocument))
arabicDoc := bytes.NewReader([]byte(arabicDocument)) arabicDoc := bytes.NewReader([]byte(arabicDocument))
@@ -787,7 +790,7 @@ func TestGraphemeReaderGraphemes(t *testing.T) {
if len(asRunes)+1 < len(graphemes) { if len(asRunes)+1 < len(graphemes) {
t.Errorf("expected <= %d graphemes, got %d", len(asRunes)+1, len(graphemes)) t.Errorf("expected <= %d graphemes, got %d", len(asRunes)+1, len(graphemes))
} }
for i := 0; i < len(graphemes)-1; i++ { for i := range len(graphemes) - 1 {
if graphemes[i] >= graphemes[i+1] { if graphemes[i] >= graphemes[i+1] {
t.Errorf("graphemes[%d](%d) >= graphemes[%d](%d)", i, graphemes[i], i+1, graphemes[i+1]) t.Errorf("graphemes[%d](%d) >= graphemes[%d](%d)", i, graphemes[i], i+1, graphemes[i+1])
} }
@@ -795,6 +798,7 @@ func TestGraphemeReaderGraphemes(t *testing.T) {
}) })
} }
} }
func BenchmarkGraphemeReaderNext(b *testing.B) { func BenchmarkGraphemeReaderNext(b *testing.B) {
latinDoc := bytes.NewReader([]byte(latinDocument)) latinDoc := bytes.NewReader([]byte(latinDocument))
arabicDoc := bytes.NewReader([]byte(arabicDocument)) arabicDoc := bytes.NewReader([]byte(arabicDocument))
@@ -831,7 +835,7 @@ func BenchmarkGraphemeReaderNext(b *testing.B) {
var paragraph []rune = make([]rune, 4096) var paragraph []rune = make([]rune, 4096)
b.Run(tc.name, func(b *testing.B) { b.Run(tc.name, func(b *testing.B) {
b.ResetTimer() b.ResetTimer()
for i := 0; i < b.N; i++ { for b.Loop() {
pr.SetSource(tc.input) pr.SetSource(tc.input)
ok := true ok := true
@@ -844,6 +848,7 @@ func BenchmarkGraphemeReaderNext(b *testing.B) {
}) })
} }
} }
func BenchmarkGraphemeReaderGraphemes(b *testing.B) { func BenchmarkGraphemeReaderGraphemes(b *testing.B) {
latinDoc := bytes.NewReader([]byte(latinDocument)) latinDoc := bytes.NewReader([]byte(latinDocument))
arabicDoc := bytes.NewReader([]byte(arabicDocument)) arabicDoc := bytes.NewReader([]byte(arabicDocument))
@@ -879,7 +884,7 @@ func BenchmarkGraphemeReaderGraphemes(b *testing.B) {
} { } {
b.Run(tc.name, func(b *testing.B) { b.Run(tc.name, func(b *testing.B) {
b.ResetTimer() b.ResetTimer()
for i := 0; i < b.N; i++ { for b.Loop() {
pr.SetSource(tc.input) pr.SetSource(tc.input)
for g := tc.read(); len(g) > 0; g = tc.read() { for g := tc.read(); len(g) > 0; g = tc.read() {
_ = g _ = g
+1 -1
View File
@@ -210,7 +210,7 @@ func (it *textIterator) paintGlyph(gtx layout.Context, shaper *text.Shaper, glyp
line = append(line, glyph) line = append(line, glyph)
} }
if glyph.Flags&text.FlagLineBreak != 0 || cap(line)-len(line) == 0 || !visibleOrBefore { if glyph.Flags&text.FlagLineBreak != 0 || cap(line)-len(line) == 0 || !visibleOrBefore {
t := op.Affine(f32.Affine2D{}.Offset(it.lineOff)).Push(gtx.Ops) t := op.Affine(f32.AffineId().Offset(it.lineOff)).Push(gtx.Ops)
path := shaper.Shape(line) path := shaper.Shape(line)
outline := clip.Outline{Path: path}.Op().Push(gtx.Ops) outline := clip.Outline{Path: path}.Op().Push(gtx.Ops)
it.material.Add(gtx.Ops) it.material.Add(gtx.Ops)
-14
View File
@@ -94,17 +94,3 @@ func (s SliderStyle) Layout(gtx layout.Context) layout.Dimensions {
return layout.Dimensions{Size: size} return layout.Dimensions{Size: size}
} }
func max(a, b int) int {
if a > b {
return a
}
return b
}
func min(a, b int) int {
if a < b {
return a
}
return b
}
+2 -2
View File
@@ -5,6 +5,7 @@ import (
"image" "image"
"io" "io"
"math" "math"
"slices"
"sort" "sort"
"unicode" "unicode"
"unicode/utf8" "unicode/utf8"
@@ -17,7 +18,6 @@ import (
"gioui.org/op/paint" "gioui.org/op/paint"
"gioui.org/text" "gioui.org/text"
"gioui.org/unit" "gioui.org/unit"
"golang.org/x/exp/slices"
"golang.org/x/image/math/fixed" "golang.org/x/image/math/fixed"
) )
@@ -717,7 +717,7 @@ func (e *textView) MoveWord(distance int, selAct selectionAction) {
} }
return r return r
} }
for ii := 0; ii < words; ii++ { for range words {
for r := next(); unicode.IsSpace(r) && !atEnd(); r = next() { for r := next(); unicode.IsSpace(r) && !atEnd(); r = next() {
e.MoveCaret(direction, 0) e.MoveCaret(direction, 0)
caret = e.closestToRune(e.caret.start) caret = e.closestToRune(e.caret.start)
+4 -4
View File
@@ -92,7 +92,7 @@ func BenchmarkLabelStatic(b *testing.B) {
runesStr := string(runes) runesStr := string(runes)
l := Label{} l := Label{}
b.ResetTimer() b.ResetTimer()
for i := 0; i < b.N; i++ { for b.Loop() {
l.Layout(gtx, cache, font, fontSize, runesStr, op.CallOp{}) l.Layout(gtx, cache, font, fontSize, runesStr, op.CallOp{})
if render { if render {
win.Frame(gtx.Ops) win.Frame(gtx.Ops)
@@ -124,7 +124,7 @@ func BenchmarkLabelDynamic(b *testing.B) {
l := Label{} l := Label{}
r := rand.New(rand.NewSource(42)) r := rand.New(rand.NewSource(42))
b.ResetTimer() b.ResetTimer()
for i := 0; i < b.N; i++ { for b.Loop() {
// simulate a constantly changing string // simulate a constantly changing string
a := r.Intn(len(runes)) a := r.Intn(len(runes))
b := r.Intn(len(runes)) b := r.Intn(len(runes))
@@ -161,7 +161,7 @@ func BenchmarkEditorStatic(b *testing.B) {
e := Editor{} e := Editor{}
e.SetText(runesStr) e.SetText(runesStr)
b.ResetTimer() b.ResetTimer()
for i := 0; i < b.N; i++ { for b.Loop() {
e.Layout(gtx, cache, font, fontSize, op.CallOp{}, op.CallOp{}) e.Layout(gtx, cache, font, fontSize, op.CallOp{}, op.CallOp{})
if render { if render {
win.Frame(gtx.Ops) win.Frame(gtx.Ops)
@@ -194,7 +194,7 @@ func BenchmarkEditorDynamic(b *testing.B) {
e.SetText(string(runes)) e.SetText(string(runes))
r := rand.New(rand.NewSource(42)) r := rand.New(rand.NewSource(42))
b.ResetTimer() b.ResetTimer()
for i := 0; i < b.N; i++ { for b.Loop() {
// simulate a constantly changing string // simulate a constantly changing string
a := r.Intn(e.Len()) a := r.Intn(e.Len())
b := r.Intn(e.Len()) b := r.Intn(e.Len())