chore(v2): vendor dependencies for offline/China builds

go mod vendor pins onnxruntime_go v1.12.1, Gio and the rest into v2/vendor
so go run/build work without hitting proxy.golang.org (blocked/slow in
China). Verified: CGO_ENABLED=1 go build -mod=vendor ./internal/spike and
GOOS=windows go build -mod=vendor ./internal/ui both pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
ila
2026-07-23 16:35:01 +08:00
co-authored by Claude Opus 4.8
parent 97c1c4a974
commit f58728cddd
972 changed files with 597802 additions and 0 deletions
+354
View File
@@ -0,0 +1,354 @@
// SPDX-License-Identifier: Unlicense OR MIT
package clip
import (
"encoding/binary"
"hash/maphash"
"image"
"math"
"gioui.org/f32"
f32internal "gioui.org/internal/f32"
"gioui.org/internal/ops"
"gioui.org/internal/scene"
"gioui.org/internal/stroke"
"gioui.org/op"
)
// Op represents a clip area. Op intersects the current clip area with
// itself.
type Op struct {
path PathSpec
outline bool
width float32
}
// Stack represents an Op pushed on the clip stack.
type Stack struct {
ops *ops.Ops
id ops.StackID
macroID uint32
}
var pathSeed maphash.Seed
func init() {
pathSeed = maphash.MakeSeed()
}
// Push saves the current clip state on the stack and updates the current
// state to the intersection of the current p.
func (p Op) Push(o *op.Ops) Stack {
id, macroID := ops.PushOp(&o.Internal, ops.ClipStack)
p.add(o)
return Stack{ops: &o.Internal, id: id, macroID: macroID}
}
func (p Op) add(o *op.Ops) {
path := p.path
if !path.hasSegments && p.width > 0 {
switch p.path.shape {
case ops.Rect:
b := f32internal.FRect(path.bounds)
var rect Path
rect.Begin(o)
rect.MoveTo(b.Min)
rect.LineTo(f32.Pt(b.Max.X, b.Min.Y))
rect.LineTo(b.Max)
rect.LineTo(f32.Pt(b.Min.X, b.Max.Y))
rect.Close()
path = rect.End()
case ops.Path:
// Nothing to do.
default:
panic("invalid empty path for shape")
}
}
bo := binary.LittleEndian
if path.hasSegments {
data := ops.Write(&o.Internal, ops.TypePathLen)
data[0] = byte(ops.TypePath)
bo.PutUint64(data[1:], path.hash)
path.spec.Add(o)
}
bounds := path.bounds
if p.width > 0 {
// Expand bounds to cover stroke.
half := int(p.width*.5 + .5)
bounds.Min.X -= half
bounds.Min.Y -= half
bounds.Max.X += half
bounds.Max.Y += half
data := ops.Write(&o.Internal, ops.TypeStrokeLen)
data[0] = byte(ops.TypeStroke)
bo := binary.LittleEndian
bo.PutUint32(data[1:], math.Float32bits(p.width))
}
data := ops.Write(&o.Internal, ops.TypeClipLen)
data[0] = byte(ops.TypeClip)
bo.PutUint32(data[1:], uint32(bounds.Min.X))
bo.PutUint32(data[5:], uint32(bounds.Min.Y))
bo.PutUint32(data[9:], uint32(bounds.Max.X))
bo.PutUint32(data[13:], uint32(bounds.Max.Y))
if p.outline {
data[17] = byte(1)
}
data[18] = byte(path.shape)
}
func (s Stack) Pop() {
ops.PopOp(s.ops, ops.ClipStack, s.id, s.macroID)
data := ops.Write(s.ops, ops.TypePopClipLen)
data[0] = byte(ops.TypePopClip)
}
type PathSpec struct {
spec op.CallOp
// hasSegments tracks whether there are any segments in the path.
hasSegments bool
bounds image.Rectangle
shape ops.Shape
hash uint64
}
// Path constructs a Op clip path described by lines and
// Bézier curves, where drawing outside the Path is discarded.
// The inside-ness of a pixel is determines by the non-zero winding rule,
// similar to the SVG rule of the same name.
//
// Path generates no garbage and can be used for dynamic paths; path
// data is stored directly in the Ops list supplied to Begin.
type Path struct {
ops *ops.Ops
contour int
pen f32.Point
macro op.MacroOp
start f32.Point
hasSegments bool
bounds f32internal.Rectangle
hash maphash.Hash
}
// Pos returns the current pen position.
func (p *Path) Pos() f32.Point { return p.pen }
// Begin the path, storing the path data and final Op into ops.
//
// Caller must also call End to finish the drawing.
// Forgetting to call it will result in a "panic: cannot mix multi ops with single ones".
func (p *Path) Begin(o *op.Ops) {
*p = Path{
ops: &o.Internal,
macro: op.Record(o),
contour: 1,
}
p.hash.SetSeed(pathSeed)
ops.BeginMulti(p.ops)
data := ops.WriteMulti(p.ops, ops.TypeAuxLen)
data[0] = byte(ops.TypeAux)
}
// End returns a PathSpec ready to use in clipping operations.
func (p *Path) End() PathSpec {
p.gap()
c := p.macro.Stop()
ops.EndMulti(p.ops)
return PathSpec{
spec: c,
hasSegments: p.hasSegments,
bounds: p.bounds.Round(),
hash: p.hash.Sum64(),
}
}
// Move moves the pen by the amount specified by delta.
func (p *Path) Move(delta f32.Point) {
to := delta.Add(p.pen)
p.MoveTo(to)
}
// MoveTo moves the pen to the specified absolute coordinate.
func (p *Path) MoveTo(to f32.Point) {
if p.pen == to {
return
}
p.gap()
p.end()
p.pen = to
p.start = to
}
func (p *Path) gap() {
if p.pen != p.start {
// A closed contour starts and ends in the same point.
// This move creates a gap in the contour, register it.
data := ops.WriteMulti(p.ops, scene.CommandSize+4)
bo := binary.LittleEndian
bo.PutUint32(data[0:], uint32(p.contour))
p.cmd(data[4:], scene.Gap(p.pen, p.start))
}
}
// end completes the current contour.
func (p *Path) end() {
p.contour++
}
// Line moves the pen by the amount specified by delta, recording a line.
func (p *Path) Line(delta f32.Point) {
to := delta.Add(p.pen)
p.LineTo(to)
}
// LineTo moves the pen to the absolute point specified, recording a line.
func (p *Path) LineTo(to f32.Point) {
if to == p.pen {
return
}
data := ops.WriteMulti(p.ops, scene.CommandSize+4)
bo := binary.LittleEndian
bo.PutUint32(data[0:], uint32(p.contour))
p.cmd(data[4:], scene.Line(p.pen, to))
p.expand(p.pen)
p.expand(to)
p.pen = to
}
func (p *Path) cmd(data []byte, c scene.Command) {
ops.EncodeCommand(data, c)
p.hash.Write(data)
}
func (p *Path) expand(pt f32.Point) {
if !p.hasSegments {
p.hasSegments = true
p.bounds = f32internal.Rectangle{Min: pt, Max: pt}
} else {
b := p.bounds
if pt.X < b.Min.X {
b.Min.X = pt.X
}
if pt.Y < b.Min.Y {
b.Min.Y = pt.Y
}
if pt.X > b.Max.X {
b.Max.X = pt.X
}
if pt.Y > b.Max.Y {
b.Max.Y = pt.Y
}
p.bounds = b
}
}
// Quad records a quadratic Bézier from the pen to end
// with the control point ctrl.
func (p *Path) Quad(ctrl, to f32.Point) {
ctrl = ctrl.Add(p.pen)
to = to.Add(p.pen)
p.QuadTo(ctrl, to)
}
// QuadTo records a quadratic Bézier from the pen to end
// with the control point ctrl, with absolute coordinates.
func (p *Path) QuadTo(ctrl, to f32.Point) {
if ctrl == p.pen && to == p.pen {
return
}
data := ops.WriteMulti(p.ops, scene.CommandSize+4)
bo := binary.LittleEndian
bo.PutUint32(data[0:], uint32(p.contour))
p.cmd(data[4:], scene.Quad(p.pen, ctrl, to))
p.expand(p.pen)
p.expand(ctrl)
p.expand(to)
p.pen = to
}
// ArcTo adds an elliptical arc to the path. The implied ellipse is defined
// by its focus points f1 and f2.
// The arc starts in the current point and ends angle radians along the ellipse boundary.
// The sign of angle determines the direction; positive being counter-clockwise,
// negative clockwise.
func (p *Path) ArcTo(f1, f2 f32.Point, angle float32) {
m, segments := stroke.ArcTransform(p.pen, f1, f2, angle)
for range segments {
p0 := p.pen
p1 := m.Transform(p0)
p2 := m.Transform(p1)
ctl := p1.Mul(2).Sub(p0.Add(p2).Mul(.5))
p.QuadTo(ctl, p2)
}
}
// Arc is like ArcTo where f1 and f2 are relative to the current position.
func (p *Path) Arc(f1, f2 f32.Point, angle float32) {
f1 = f1.Add(p.pen)
f2 = f2.Add(p.pen)
p.ArcTo(f1, f2, angle)
}
// Cube records a cubic Bézier from the pen through
// two control points ending in to.
func (p *Path) Cube(ctrl0, ctrl1, to f32.Point) {
p.CubeTo(p.pen.Add(ctrl0), p.pen.Add(ctrl1), p.pen.Add(to))
}
// CubeTo records a cubic Bézier from the pen through
// two control points ending in to, with absolute coordinates.
func (p *Path) CubeTo(ctrl0, ctrl1, to f32.Point) {
if ctrl0 == p.pen && ctrl1 == p.pen && to == p.pen {
return
}
data := ops.WriteMulti(p.ops, scene.CommandSize+4)
bo := binary.LittleEndian
bo.PutUint32(data[0:], uint32(p.contour))
p.cmd(data[4:], scene.Cubic(p.pen, ctrl0, ctrl1, to))
p.expand(p.pen)
p.expand(ctrl0)
p.expand(ctrl1)
p.expand(to)
p.pen = to
}
// Close closes the path contour.
func (p *Path) Close() {
if p.pen != p.start {
p.LineTo(p.start)
}
p.end()
}
// Stroke represents a stroked path.
type Stroke struct {
Path PathSpec
// Width of the stroked path.
Width float32
}
// Op returns a clip operation representing the stroke.
func (s Stroke) Op() Op {
return Op{
path: s.Path,
width: s.Width,
}
}
// Outline represents the area inside of a path, according to the
// non-zero winding rule.
type Outline struct {
Path PathSpec
}
// Op returns a clip operation representing the outline.
func (o Outline) Op() Op {
return Op{
path: o.Path,
outline: true,
}
}
+14
View File
@@ -0,0 +1,14 @@
// SPDX-License-Identifier: Unlicense OR MIT
/*
Package clip provides operations for defining areas that applies to operations
such as paints and pointer handlers.
The current clip is initially the infinite set. Pushing an Op sets the clip
to the intersection of the current clip and pushed clip area. Popping the
area restores the clip to its state before pushing.
General clipping areas are constructed with Path. Common cases such as
rectangular clip areas also exist as convenient constructors.
*/
package clip
+175
View File
@@ -0,0 +1,175 @@
// SPDX-License-Identifier: Unlicense OR MIT
package clip
import (
"image"
"math"
"gioui.org/f32"
f32internal "gioui.org/internal/f32"
"gioui.org/internal/ops"
"gioui.org/op"
)
// Rect represents the clip area of a pixel-aligned rectangle.
type Rect image.Rectangle
// Op returns the op for the rectangle.
func (r Rect) Op() Op {
return Op{
outline: true,
path: r.Path(),
}
}
// Push the clip operation on the clip stack.
func (r Rect) Push(ops *op.Ops) Stack {
return r.Op().Push(ops)
}
// Path returns the PathSpec for the rectangle.
func (r Rect) Path() PathSpec {
return PathSpec{
shape: ops.Rect,
bounds: image.Rectangle(r),
}
}
// UniformRRect returns an RRect with all corner radii set to the
// provided radius.
func UniformRRect(rect image.Rectangle, radius int) RRect {
return RRect{
Rect: rect,
SE: radius,
SW: radius,
NE: radius,
NW: radius,
}
}
// RRect represents the clip area of a rectangle with rounded
// corners.
//
// Specify a square with corner radii equal to half the square size to
// construct a circular clip area.
type RRect struct {
Rect image.Rectangle
// The corner radii.
SE, SW, NW, NE int
}
// Op returns the op for the rounded rectangle.
func (rr RRect) Op(ops *op.Ops) Op {
if rr.SE == 0 && rr.SW == 0 && rr.NW == 0 && rr.NE == 0 {
return Rect(rr.Rect).Op()
}
return Outline{Path: rr.Path(ops)}.Op()
}
// Push the rectangle clip on the clip stack.
func (rr RRect) Push(ops *op.Ops) Stack {
return rr.Op(ops).Push(ops)
}
// Path returns the PathSpec for the rounded rectangle.
func (rr RRect) Path(ops *op.Ops) PathSpec {
var p Path
p.Begin(ops)
// https://pomax.github.io/bezierinfo/#circles_cubic.
const q = 4 * (math.Sqrt2 - 1) / 3
const iq = 1 - q
se, sw, nw, ne := float32(rr.SE), float32(rr.SW), float32(rr.NW), float32(rr.NE)
rrf := f32internal.FRect(rr.Rect)
w, n, e, s := rrf.Min.X, rrf.Min.Y, rrf.Max.X, rrf.Max.Y
p.MoveTo(f32.Point{X: w + nw, Y: n})
p.LineTo(f32.Point{X: e - ne, Y: n}) // N
p.CubeTo( // NE
f32.Point{X: e - ne*iq, Y: n},
f32.Point{X: e, Y: n + ne*iq},
f32.Point{X: e, Y: n + ne})
p.LineTo(f32.Point{X: e, Y: s - se}) // E
p.CubeTo( // SE
f32.Point{X: e, Y: s - se*iq},
f32.Point{X: e - se*iq, Y: s},
f32.Point{X: e - se, Y: s})
p.LineTo(f32.Point{X: w + sw, Y: s}) // S
p.CubeTo( // SW
f32.Point{X: w + sw*iq, Y: s},
f32.Point{X: w, Y: s - sw*iq},
f32.Point{X: w, Y: s - sw})
p.LineTo(f32.Point{X: w, Y: n + nw}) // W
p.CubeTo( // NW
f32.Point{X: w, Y: n + nw*iq},
f32.Point{X: w + nw*iq, Y: n},
f32.Point{X: w + nw, Y: n})
return p.End()
}
// Ellipse represents the largest axis-aligned ellipse that
// is contained in its bounds.
type Ellipse image.Rectangle
// Op returns the op for the filled ellipse.
func (e Ellipse) Op(ops *op.Ops) Op {
return Outline{Path: e.Path(ops)}.Op()
}
// Push the filled ellipse clip op on the clip stack.
func (e Ellipse) Push(ops *op.Ops) Stack {
return e.Op(ops).Push(ops)
}
// Path constructs a path for the ellipse.
func (e Ellipse) Path(o *op.Ops) PathSpec {
bounds := image.Rectangle(e)
if bounds.Dx() == 0 || bounds.Dy() == 0 {
return PathSpec{shape: ops.Rect}
}
var p Path
p.Begin(o)
bf := f32internal.FRect(bounds)
center := bf.Max.Add(bf.Min).Mul(.5)
diam := bf.Dx()
r := diam * .5
// We'll model the ellipse as a circle scaled in the Y
// direction.
scale := bf.Dy() / diam
// https://pomax.github.io/bezierinfo/#circles_cubic.
const q = 4 * (math.Sqrt2 - 1) / 3
curve := r * q
top := f32.Point{X: center.X, Y: center.Y - r*scale}
p.MoveTo(top)
p.CubeTo(
f32.Point{X: center.X + curve, Y: center.Y - r*scale},
f32.Point{X: center.X + r, Y: center.Y - curve*scale},
f32.Point{X: center.X + r, Y: center.Y},
)
p.CubeTo(
f32.Point{X: center.X + r, Y: center.Y + curve*scale},
f32.Point{X: center.X + curve, Y: center.Y + r*scale},
f32.Point{X: center.X, Y: center.Y + r*scale},
)
p.CubeTo(
f32.Point{X: center.X - curve, Y: center.Y + r*scale},
f32.Point{X: center.X - r, Y: center.Y + curve*scale},
f32.Point{X: center.X - r, Y: center.Y},
)
p.CubeTo(
f32.Point{X: center.X - r, Y: center.Y - curve*scale},
f32.Point{X: center.X - curve, Y: center.Y - r*scale},
top,
)
ellipse := p.End()
ellipse.shape = ops.Ellipse
return ellipse
}
+230
View File
@@ -0,0 +1,230 @@
// SPDX-License-Identifier: Unlicense OR MIT
/*
Package op implements operations for updating a user interface.
Gio programs use operations, or ops, for describing their user
interfaces. There are operations for drawing, defining input
handlers, changing window properties as well as operations for
controlling the execution of other operations.
Ops represents a list of operations. The most important use
for an Ops list is to describe a complete user interface update
to a ui/app.Window's Update method.
Drawing a colored square:
import "gioui.org/unit"
import "gioui.org/app"
import "gioui.org/op/paint"
var w app.Window
var e system.FrameEvent
ops := new(op.Ops)
...
ops.Reset()
paint.ColorOp{Color: ...}.Add(ops)
paint.PaintOp{Rect: ...}.Add(ops)
e.Frame(ops)
# State
An Ops list can be viewed as a very simple virtual machine: it has state such
as transformation and color and execution flow can be controlled with macros.
Some state, such as the current color, is modified directly by operations with
Add methods. Other state, such as transformation and clip shape, are
represented by stacks.
This example sets the simple color state and pushes an offset to the
transformation stack.
ops := new(op.Ops)
// Set the color.
paint.ColorOp{...}.Add(ops)
// Apply an offset to subsequent operations.
stack := op.Offset(...).Push(ops)
...
// Undo the offset transformation.
stack.Pop()
The MacroOp records a list of operations to be executed later:
ops := new(op.Ops)
macro := op.Record(ops)
// Record operations by adding them.
...
// End recording.
call := macro.Stop()
// replay the recorded operations:
call.Add(ops)
*/
package op
import (
"encoding/binary"
"image"
"math"
"time"
"gioui.org/f32"
"gioui.org/internal/ops"
)
// Ops holds a list of operations. Operations are stored in
// serialized form to avoid garbage during construction of
// the ops list.
type Ops struct {
// Internal is for internal use, despite being exported.
Internal ops.Ops
}
// MacroOp records a list of operations for later use.
type MacroOp struct {
ops *ops.Ops
id ops.StackID
pc ops.PC
}
// CallOp invokes the operations recorded by Record.
type CallOp struct {
// Ops is the list of operations to invoke.
ops *ops.Ops
start ops.PC
end ops.PC
}
// InvalidateCmd requests a redraw at the given time. Use
// the zero value to request an immediate redraw.
type InvalidateCmd struct {
At time.Time
}
// TransformOp represents a transformation that can be pushed on the
// transformation stack.
type TransformOp struct {
t f32.Affine2D
}
// TransformStack represents a TransformOp pushed on the transformation stack.
type TransformStack struct {
id ops.StackID
macroID uint32
ops *ops.Ops
}
// Defer executes c after all other operations have completed, including
// previously deferred operations.
// Defer saves the transformation stack and pushes it prior to executing
// c. All other operation state is reset.
//
// Note that deferred operations are executed in first-in-first-out order,
// unlike the Go facility of the same name.
func Defer(o *Ops, c CallOp) {
if c.ops == nil {
return
}
state := ops.Save(&o.Internal)
// Wrap c in a macro that loads the saved state before execution.
m := Record(o)
state.Load()
c.Add(o)
c = m.Stop()
// A Defer is recorded as a TypeDefer followed by the
// wrapped macro.
data := ops.Write(&o.Internal, ops.TypeDeferLen)
data[0] = byte(ops.TypeDefer)
c.Add(o)
}
// Reset the Ops, preparing it for re-use. Reset invalidates
// any recorded macros.
func (o *Ops) Reset() {
ops.Reset(&o.Internal)
}
// Record a macro of operations.
func Record(o *Ops) MacroOp {
m := MacroOp{
ops: &o.Internal,
id: ops.PushMacro(&o.Internal),
pc: ops.PCFor(&o.Internal),
}
// Reserve room for a macro definition. Updated in Stop.
data := ops.Write(m.ops, ops.TypeMacroLen)
data[0] = byte(ops.TypeMacro)
return m
}
// Stop ends a previously started recording and returns an
// operation for replaying it.
func (m MacroOp) Stop() CallOp {
ops.PopMacro(m.ops, m.id)
ops.FillMacro(m.ops, m.pc)
return CallOp{
ops: m.ops,
// Skip macro header.
start: m.pc.Add(ops.TypeMacro),
end: ops.PCFor(m.ops),
}
}
// Add the recorded list of operations. Add
// panics if the Ops containing the recording
// has been reset.
func (c CallOp) Add(o *Ops) {
if c.ops == nil {
return
}
ops.AddCall(&o.Internal, c.ops, c.start, c.end)
}
// Offset converts an offset to a TransformOp.
func Offset(off image.Point) TransformOp {
offf := f32.Pt(float32(off.X), float32(off.Y))
return Affine(f32.AffineId().Offset(offf))
}
// Affine creates a TransformOp representing the transformation a.
func Affine(a f32.Affine2D) TransformOp {
return TransformOp{t: a}
}
// Push the current transformation to the stack and then multiply the
// current transformation with t.
func (t TransformOp) Push(o *Ops) TransformStack {
id, macroID := ops.PushOp(&o.Internal, ops.TransStack)
t.add(o, true)
return TransformStack{ops: &o.Internal, id: id, macroID: macroID}
}
// Add is like Push except it doesn't push the current transformation to the
// stack.
func (t TransformOp) Add(o *Ops) {
t.add(o, false)
}
func (t TransformOp) add(o *Ops, push bool) {
data := ops.Write(&o.Internal, ops.TypeTransformLen)
data[0] = byte(ops.TypeTransform)
if push {
data[1] = 1
}
bo := binary.LittleEndian
a, b, c, d, e, f := t.t.Elems()
bo.PutUint32(data[2:], math.Float32bits(a))
bo.PutUint32(data[2+4*1:], math.Float32bits(b))
bo.PutUint32(data[2+4*2:], math.Float32bits(c))
bo.PutUint32(data[2+4*3:], math.Float32bits(d))
bo.PutUint32(data[2+4*4:], math.Float32bits(e))
bo.PutUint32(data[2+4*5:], math.Float32bits(f))
}
func (t TransformStack) Pop() {
ops.PopOp(t.ops, ops.TransStack, t.id, t.macroID)
data := ops.Write(t.ops, ops.TypePopTransformLen)
data[0] = byte(ops.TypePopTransform)
}
func (InvalidateCmd) ImplementsCommand() {}
+15
View File
@@ -0,0 +1,15 @@
// SPDX-License-Identifier: Unlicense OR MIT
/*
Package paint provides drawing operations for 2D graphics.
The PaintOp operation fills the current clip with the current brush, taking the
current transformation into account. Drawing outside the current clip area is
ignored.
The current brush is set by either a ColorOp for a constant color, or
ImageOp for an image, or LinearGradientOp for gradients.
All color.NRGBA values are in the sRGB color space.
*/
package paint
+195
View File
@@ -0,0 +1,195 @@
// SPDX-License-Identifier: Unlicense OR MIT
package paint
import (
"encoding/binary"
"image"
"image/color"
"image/draw"
"math"
"gioui.org/f32"
"gioui.org/internal/ops"
"gioui.org/op"
"gioui.org/op/clip"
)
// ImageFilter is the scaling filter for images.
type ImageFilter byte
const (
// FilterLinear uses linear interpolation for scaling.
FilterLinear ImageFilter = iota
// FilterNearest uses nearest neighbor interpolation for scaling.
FilterNearest
)
// ImageOp sets the brush to an image.
type ImageOp struct {
Filter ImageFilter
uniform bool
color color.NRGBA
src *image.RGBA
// handle is a key to uniquely identify this ImageOp
// in a map of cached textures.
handle any
}
// ColorOp sets the brush to a constant color.
type ColorOp struct {
Color color.NRGBA
}
// LinearGradientOp sets the brush to a gradient starting at stop1 with color1 and
// ending at stop2 with color2.
type LinearGradientOp struct {
Stop1 f32.Point
Color1 color.NRGBA
Stop2 f32.Point
Color2 color.NRGBA
}
// PaintOp fills the current clip area with the current brush.
type PaintOp struct{}
// OpacityStack represents an opacity applied to all painting operations
// until Pop is called.
type OpacityStack struct {
id ops.StackID
macroID uint32
ops *ops.Ops
}
// NewImageOp creates an ImageOp backed by src.
//
// NewImageOp assumes the backing image is immutable, and may cache a
// copy of its contents in a GPU-friendly way. Create new ImageOps to
// ensure that changes to an image is reflected in the display of
// it.
func NewImageOp(src image.Image) ImageOp {
switch src := src.(type) {
case *image.Uniform:
col := color.NRGBAModel.Convert(src.C).(color.NRGBA)
return ImageOp{
uniform: true,
color: col,
}
case *image.RGBA:
return ImageOp{
src: src,
handle: new(int),
}
}
sz := src.Bounds().Size()
// Copy the image into a GPU friendly format.
dst := image.NewRGBA(image.Rectangle{
Max: sz,
})
draw.Draw(dst, dst.Bounds(), src, src.Bounds().Min, draw.Src)
return ImageOp{
src: dst,
handle: new(int),
}
}
func (i ImageOp) Size() image.Point {
if i.src == nil {
return image.Point{}
}
return i.src.Bounds().Size()
}
func (i ImageOp) Add(o *op.Ops) {
if i.uniform {
ColorOp{
Color: i.color,
}.Add(o)
return
} else if i.src == nil || i.src.Bounds().Empty() {
return
}
data := ops.Write2(&o.Internal, ops.TypeImageLen, i.src, i.handle)
data[0] = byte(ops.TypeImage)
data[1] = byte(i.Filter)
}
func (c ColorOp) Add(o *op.Ops) {
data := ops.Write(&o.Internal, ops.TypeColorLen)
data[0] = byte(ops.TypeColor)
data[1] = c.Color.R
data[2] = c.Color.G
data[3] = c.Color.B
data[4] = c.Color.A
}
func (c LinearGradientOp) Add(o *op.Ops) {
data := ops.Write(&o.Internal, ops.TypeLinearGradientLen)
data[0] = byte(ops.TypeLinearGradient)
bo := binary.LittleEndian
bo.PutUint32(data[1:], math.Float32bits(c.Stop1.X))
bo.PutUint32(data[5:], math.Float32bits(c.Stop1.Y))
bo.PutUint32(data[9:], math.Float32bits(c.Stop2.X))
bo.PutUint32(data[13:], math.Float32bits(c.Stop2.Y))
data[17+0] = c.Color1.R
data[17+1] = c.Color1.G
data[17+2] = c.Color1.B
data[17+3] = c.Color1.A
data[21+0] = c.Color2.R
data[21+1] = c.Color2.G
data[21+2] = c.Color2.B
data[21+3] = c.Color2.A
}
func (d PaintOp) Add(o *op.Ops) {
data := ops.Write(&o.Internal, ops.TypePaintLen)
data[0] = byte(ops.TypePaint)
}
// FillShape fills the clip shape with a color.
func FillShape(ops *op.Ops, c color.NRGBA, shape clip.Op) {
defer shape.Push(ops).Pop()
Fill(ops, c)
}
// Fill paints an infinitely large plane with the provided color. It
// is intended to be used with a clip.Op already in place to limit
// the painted area. Use FillShape unless you need to paint several
// times within the same clip.Op.
func Fill(ops *op.Ops, c color.NRGBA) {
ColorOp{Color: c}.Add(ops)
PaintOp{}.Add(ops)
}
// PushOpacity creates a drawing layer with an opacity in the range [0;1].
// The layer includes every subsequent drawing operation until [OpacityStack.Pop]
// is called.
//
// The layer is drawn in two steps. First, the layer operations are
// drawn to a separate image. Then, the image is blended on top of
// the frame, with the opacity used as the blending factor.
func PushOpacity(o *op.Ops, opacity float32) OpacityStack {
if opacity > 1 {
opacity = 1
}
if opacity < 0 {
opacity = 0
}
id, macroID := ops.PushOp(&o.Internal, ops.OpacityStack)
data := ops.Write(&o.Internal, ops.TypePushOpacityLen)
bo := binary.LittleEndian
data[0] = byte(ops.TypePushOpacity)
bo.PutUint32(data[1:], math.Float32bits(opacity))
return OpacityStack{ops: &o.Internal, id: id, macroID: macroID}
}
func (t OpacityStack) Pop() {
ops.PopOp(t.ops, ops.OpacityStack, t.id, t.macroID)
data := ops.Write(t.ops, ops.TypePopOpacityLen)
data[0] = byte(ops.TypePopOpacity)
}