all: [API] change clip.RRect and UniformRRect to take integer coordinates

Like the change to op.Offset before this, clip.RRect and UniformRRect
is usually used with integer coordinates. Change to integer coordinates
to eliminate many useless conversions to float32.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
Elias Naur
2022-04-26 09:36:52 +02:00
parent a63e0cb44a
commit 48a8540a68
16 changed files with 103 additions and 139 deletions
+4 -11
View File
@@ -7,7 +7,6 @@ import (
"image/color" "image/color"
"testing" "testing"
"gioui.org/f32"
"gioui.org/internal/f32color" "gioui.org/internal/f32color"
"gioui.org/op" "gioui.org/op"
"gioui.org/op/clip" "gioui.org/op/clip"
@@ -50,20 +49,14 @@ func TestClipping(t *testing.T) {
var ops op.Ops var ops op.Ops
paint.ColorOp{Color: col}.Add(&ops) paint.ColorOp{Color: col}.Add(&ops)
clip.RRect{ clip.RRect{
Rect: f32.Rectangle{ Rect: image.Rect(50, 50, 250, 250),
Min: f32.Point{X: 50, Y: 50}, SE: 75,
Max: f32.Point{X: 250, Y: 250},
},
SE: 75,
}.Push(&ops) }.Push(&ops)
paint.PaintOp{}.Add(&ops) paint.PaintOp{}.Add(&ops)
paint.ColorOp{Color: col2}.Add(&ops) paint.ColorOp{Color: col2}.Add(&ops)
clip.RRect{ clip.RRect{
Rect: f32.Rectangle{ Rect: image.Rect(100, 100, 350, 350),
Min: f32.Point{X: 100, Y: 100}, NW: 75,
Max: f32.Point{X: 350, Y: 350},
},
NW: 75,
}.Push(&ops) }.Push(&ops)
paint.PaintOp{}.Add(&ops) paint.PaintOp{}.Add(&ops)
if err := w.Frame(&ops); err != nil { if err := w.Frame(&ops); err != nil {
+4 -4
View File
@@ -161,7 +161,7 @@ func draw1000Circles(gtx layout.Context) {
for y := 0; y < 10; y++ { for y := 0; y < 10; y++ {
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: f32.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),
) )
op.Offset(image.Pt(0, 100)).Add(ops) op.Offset(image.Pt(0, 100)).Add(ops)
} }
@@ -172,7 +172,7 @@ func draw1000CirclesInstanced(gtx layout.Context) {
ops := gtx.Ops ops := gtx.Ops
r := op.Record(ops) r := op.Record(ops)
cl := clip.RRect{Rect: f32.Rect(0, 0, 10, 10), NE: 5, SE: 5, SW: 5, NW: 5}.Push(ops) cl := clip.RRect{Rect: image.Rect(0, 0, 10, 10), NE: 5, SE: 5, SW: 5, NW: 5}.Push(ops)
paint.PaintOp{}.Add(ops) paint.PaintOp{}.Add(ops)
cl.Pop() cl.Pop()
c := r.Stop() c := r.Stop()
@@ -207,7 +207,7 @@ func drawIndividualShapes(gtx layout.Context, th *material.Theme) chan op.CallOp
for y := 0; y < 9; y++ { for y := 0; y < 9; y++ {
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: f32.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),
) )
op.Offset(image.Pt(0, 50)).Add(ops) op.Offset(image.Pt(0, 50)).Add(ops)
} }
@@ -224,7 +224,7 @@ func drawShapeInstances(gtx layout.Context, th *material.Theme) chan op.CallOp {
co := op.Record(ops) co := op.Record(ops)
r := op.Record(ops) r := op.Record(ops)
cl := clip.RRect{Rect: f32.Rect(0, 0, 25, 25), NE: 10, SE: 10, SW: 10, NW: 10}.Push(ops) cl := clip.RRect{Rect: image.Rect(0, 0, 25, 25), NE: 10, SE: 10, SW: 10, NW: 10}.Push(ops)
paint.PaintOp{}.Add(ops) paint.PaintOp{}.Add(ops)
cl.Pop() cl.Pop()
c := r.Stop() c := r.Stop()
+7 -7
View File
@@ -29,7 +29,7 @@ func TestPaintRect(t *testing.T) {
func TestPaintClippedRect(t *testing.T) { func TestPaintClippedRect(t *testing.T) {
run(t, func(o *op.Ops) { run(t, func(o *op.Ops) {
defer clip.RRect{Rect: f32.Rect(25, 25, 60, 60)}.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()) paint.FillShape(o, red, clip.Rect(image.Rect(0, 0, 50, 50)).Op())
}, func(r result) { }, func(r result) {
r.expect(0, 0, transparent) r.expect(0, 0, transparent)
@@ -42,8 +42,8 @@ func TestPaintClippedRect(t *testing.T) {
func TestPaintClippedCircle(t *testing.T) { func TestPaintClippedCircle(t *testing.T) {
run(t, func(o *op.Ops) { run(t, func(o *op.Ops) {
r := float32(10) const r = 10
defer clip.RRect{Rect: f32.Rect(20, 20, 40, 40), SE: r, SW: r, NW: r, NE: r}.Push(o).Pop() defer clip.RRect{Rect: image.Rect(20, 20, 40, 40), SE: r, SW: r, NW: r, NE: r}.Push(o).Pop()
defer clip.Rect(image.Rect(0, 0, 30, 50)).Push(o).Pop() defer clip.Rect(image.Rect(0, 0, 30, 50)).Push(o).Pop()
paint.Fill(o, red) paint.Fill(o, red)
}, func(r result) { }, func(r result) {
@@ -126,10 +126,10 @@ func TestTexturedStrokeClipped(t *testing.T) {
smallSquares.Add(o) smallSquares.Add(o)
defer op.Offset(image.Pt(50, 50)).Push(o).Pop() defer op.Offset(image.Pt(50, 50)).Push(o).Pop()
defer clip.Stroke{ defer clip.Stroke{
Path: clip.RRect{Rect: f32.Rect(0, 0, 30, 30)}.Path(o), Path: clip.RRect{Rect: image.Rect(0, 0, 30, 30)}.Path(o),
Width: 10, Width: 10,
}.Op().Push(o).Pop() }.Op().Push(o).Pop()
defer clip.RRect{Rect: f32.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) { }, func(r result) {
@@ -141,7 +141,7 @@ func TestTexturedStroke(t *testing.T) {
smallSquares.Add(o) smallSquares.Add(o)
defer op.Offset(image.Pt(50, 50)).Push(o).Pop() defer op.Offset(image.Pt(50, 50)).Push(o).Pop()
defer clip.Stroke{ defer clip.Stroke{
Path: clip.RRect{Rect: f32.Rect(0, 0, 30, 30)}.Path(o), Path: clip.RRect{Rect: image.Rect(0, 0, 30, 30)}.Path(o),
Width: 10, Width: 10,
}.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()
@@ -153,7 +153,7 @@ func TestTexturedStroke(t *testing.T) {
func TestPaintClippedTexture(t *testing.T) { func TestPaintClippedTexture(t *testing.T) {
run(t, func(o *op.Ops) { run(t, func(o *op.Ops) {
squares.Add(o) squares.Add(o)
defer clip.RRect{Rect: f32.Rect(0, 0, 40, 40)}.Push(o).Pop() defer clip.RRect{Rect: image.Rect(0, 0, 40, 40)}.Push(o).Pop()
defer scale(80.0/512, 80.0/512).Push(o).Pop() defer scale(80.0/512, 80.0/512).Push(o).Pop()
paint.PaintOp{}.Add(o) paint.PaintOp{}.Add(o)
}, func(r result) { }, func(r result) {
+3 -3
View File
@@ -143,7 +143,7 @@ func constSqPath() clip.Op {
func constSqCirc() clip.Op { func constSqCirc() clip.Op {
innerOps := new(op.Ops) innerOps := new(op.Ops)
return clip.RRect{Rect: f32.Rect(0, 0, 40, 40), return clip.RRect{Rect: image.Rect(0, 0, 40, 40),
NW: 20, NE: 20, SW: 20, SE: 20}.Op(innerOps) NW: 20, NE: 20, SW: 20, SE: 20}.Op(innerOps)
} }
@@ -204,7 +204,7 @@ func TestBuildOffscreen(t *testing.T) {
func TestNegativeOverlaps(t *testing.T) { func TestNegativeOverlaps(t *testing.T) {
run(t, func(ops *op.Ops) { run(t, func(ops *op.Ops) {
defer clip.RRect{Rect: f32.Rect(50, 50, 100, 100)}.Push(ops).Pop() defer clip.RRect{Rect: image.Rect(50, 50, 100, 100)}.Push(ops).Pop()
clip.Rect(image.Rect(0, 120, 100, 122)).Push(ops).Pop() clip.Rect(image.Rect(0, 120, 100, 122)).Push(ops).Pop()
paint.PaintOp{}.Add(ops) paint.PaintOp{}.Add(ops)
}, func(r result) { }, func(r result) {
@@ -257,7 +257,7 @@ func TestLinearGradient(t *testing.T) {
Stop2: f32.Pt(gr.Max.X, gr.Min.Y), Stop2: f32.Pt(gr.Max.X, gr.Min.Y),
Color2: g.To, Color2: g.To,
}.Add(ops) }.Add(ops)
cl := clip.RRect{Rect: gr}.Push(ops) cl := clip.RRect{Rect: gr.Round()}.Push(ops)
t1 := op.Affine(f32.Affine2D{}.Offset(pixelAligned.Min)).Push(ops) t1 := op.Affine(f32.Affine2D{}.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)
+7 -7
View File
@@ -52,7 +52,7 @@ func TestPaintShear(t *testing.T) {
func TestClipPaintOffset(t *testing.T) { func TestClipPaintOffset(t *testing.T) {
run(t, func(o *op.Ops) { run(t, func(o *op.Ops) {
defer clip.RRect{Rect: f32.Rect(10, 10, 30, 30)}.Push(o).Pop() defer clip.RRect{Rect: image.Rect(10, 10, 30, 30)}.Push(o).Pop()
defer op.Offset(image.Pt(20, 20)).Push(o).Pop() defer op.Offset(image.Pt(20, 20)).Push(o).Pop()
paint.FillShape(o, red, clip.Rect(image.Rect(0, 0, 100, 100)).Op()) paint.FillShape(o, red, clip.Rect(image.Rect(0, 0, 100, 100)).Op())
}, func(r result) { }, func(r result) {
@@ -66,7 +66,7 @@ func TestClipPaintOffset(t *testing.T) {
func TestClipOffset(t *testing.T) { func TestClipOffset(t *testing.T) {
run(t, func(o *op.Ops) { run(t, func(o *op.Ops) {
defer op.Offset(image.Pt(20, 20)).Push(o).Pop() defer op.Offset(image.Pt(20, 20)).Push(o).Pop()
defer clip.RRect{Rect: f32.Rect(10, 10, 30, 30)}.Push(o).Pop() defer clip.RRect{Rect: image.Rect(10, 10, 30, 30)}.Push(o).Pop()
paint.FillShape(o, red, clip.Rect(image.Rect(0, 0, 100, 100)).Op()) paint.FillShape(o, red, clip.Rect(image.Rect(0, 0, 100, 100)).Op())
}, func(r result) { }, func(r result) {
r.expect(0, 0, transparent) r.expect(0, 0, transparent)
@@ -81,7 +81,7 @@ 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.Affine2D{}.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: f32.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())
}, func(r result) { }, func(r result) {
r.expect(19+10, 19+10, transparent) r.expect(19+10, 19+10, transparent)
@@ -94,7 +94,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.Affine2D{}.Rotate(f32.Pt(40, 40), -math.Pi/4)).Push(o).Pop()
defer clip.RRect{Rect: f32.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) {
r.expect(39, 39, transparent) r.expect(39, 39, transparent)
@@ -148,7 +148,7 @@ func TestRotateClipTexture(t *testing.T) {
squares.Add(o) squares.Add(o)
a := f32.Affine2D{}.Rotate(f32.Pt(40, 40), math.Pi/8) a := f32.Affine2D{}.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: f32.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.Affine2D{}.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)
@@ -166,11 +166,11 @@ func TestComplicatedTransform(t *testing.T) {
run(t, func(o *op.Ops) { run(t, func(o *op.Ops) {
squares.Add(o) squares.Add(o)
defer clip.RRect{Rect: f32.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.Affine2D{}.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: f32.Rect(0, 0, 50, 40)}.Push(o).Pop() defer clip.RRect{Rect: image.Rect(0, 0, 50, 40)}.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)
+1 -1
View File
@@ -741,7 +741,7 @@ func TestEllipse(t *testing.T) {
var ops op.Ops var ops op.Ops
h := new(int) h := new(int)
cl := clip.Ellipse(f32.Rect(0, 0, 100, 100)).Push(&ops) cl := clip.Ellipse(image.Rect(0, 0, 100, 100)).Push(&ops)
pointer.InputOp{Tag: h, Types: pointer.Press}.Add(&ops) pointer.InputOp{Tag: h, Types: pointer.Press}.Add(&ops)
cl.Pop() cl.Pop()
var r Router var r Router
+13 -24
View File
@@ -37,7 +37,7 @@ func (r Rect) Path() PathSpec {
// UniformRRect returns an RRect with all corner radii set to the // UniformRRect returns an RRect with all corner radii set to the
// provided radius. // provided radius.
func UniformRRect(rect f32.Rectangle, radius float32) RRect { func UniformRRect(rect image.Rectangle, radius int) RRect {
return RRect{ return RRect{
Rect: rect, Rect: rect,
SE: radius, SE: radius,
@@ -53,22 +53,15 @@ func UniformRRect(rect f32.Rectangle, radius float32) RRect {
// Specify a square with corner radii equal to half the square size to // Specify a square with corner radii equal to half the square size to
// construct a circular clip area. // construct a circular clip area.
type RRect struct { type RRect struct {
Rect f32.Rectangle Rect image.Rectangle
// The corner radii. // The corner radii.
SE, SW, NW, NE float32 SE, SW, NW, NE int
} }
// Op returns the op for the rounded rectangle. // Op returns the op for the rounded rectangle.
func (rr RRect) Op(ops *op.Ops) Op { func (rr RRect) Op(ops *op.Ops) Op {
if rr.SE == 0 && rr.SW == 0 && rr.NW == 0 && rr.NE == 0 { if rr.SE == 0 && rr.SW == 0 && rr.NW == 0 && rr.NE == 0 {
r := image.Rectangle{ return Rect(rr.Rect).Op()
Min: image.Point{X: int(rr.Rect.Min.X), Y: int(rr.Rect.Min.Y)},
Max: image.Point{X: int(rr.Rect.Max.X), Y: int(rr.Rect.Max.Y)},
}
// Only use Rect if rr is pixel-aligned, as Rect is guaranteed to be.
if fPt(r.Min) == rr.Rect.Min && fPt(r.Max) == rr.Rect.Max {
return Rect(r).Op()
}
} }
return Outline{Path: rr.Path(ops)}.Op() return Outline{Path: rr.Path(ops)}.Op()
} }
@@ -87,8 +80,9 @@ func (rr RRect) Path(ops *op.Ops) PathSpec {
const q = 4 * (math.Sqrt2 - 1) / 3 const q = 4 * (math.Sqrt2 - 1) / 3
const iq = 1 - q const iq = 1 - q
se, sw, nw, ne := rr.SE, rr.SW, rr.NW, rr.NE se, sw, nw, ne := float32(rr.SE), float32(rr.SW), float32(rr.NW), float32(rr.NE)
w, n, e, s := rr.Rect.Min.X, rr.Rect.Min.Y, rr.Rect.Max.X, rr.Rect.Max.Y rrf := 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.MoveTo(f32.Point{X: w + nw, Y: n})
p.LineTo(f32.Point{X: e - ne, Y: n}) // N p.LineTo(f32.Point{X: e - ne, Y: n}) // N
@@ -117,7 +111,7 @@ func (rr RRect) Path(ops *op.Ops) PathSpec {
// Ellipse represents the largest axis-aligned ellipse that // Ellipse represents the largest axis-aligned ellipse that
// is contained in its bounds. // is contained in its bounds.
type Ellipse f32.Rectangle type Ellipse image.Rectangle
// Op returns the op for the filled ellipse. // Op returns the op for the filled ellipse.
func (e Ellipse) Op(ops *op.Ops) Op { func (e Ellipse) Op(ops *op.Ops) Op {
@@ -131,7 +125,7 @@ func (e Ellipse) Push(ops *op.Ops) Stack {
// Path constructs a path for the ellipse. // Path constructs a path for the ellipse.
func (e Ellipse) Path(o *op.Ops) PathSpec { func (e Ellipse) Path(o *op.Ops) PathSpec {
bounds := f32.Rectangle(e) bounds := image.Rectangle(e)
if bounds.Dx() == 0 || bounds.Dy() == 0 { if bounds.Dx() == 0 || bounds.Dy() == 0 {
return PathSpec{shape: ops.Rect} return PathSpec{shape: ops.Rect}
} }
@@ -139,12 +133,13 @@ func (e Ellipse) Path(o *op.Ops) PathSpec {
var p Path var p Path
p.Begin(o) p.Begin(o)
center := bounds.Max.Add(bounds.Min).Mul(.5) bf := frect(bounds)
diam := bounds.Dx() center := bf.Max.Add(bf.Min).Mul(.5)
diam := bf.Dx()
r := diam * .5 r := diam * .5
// We'll model the ellipse as a circle scaled in the Y // We'll model the ellipse as a circle scaled in the Y
// direction. // direction.
scale := bounds.Dy() / diam scale := bf.Dy() / diam
// https://pomax.github.io/bezierinfo/#circles_cubic. // https://pomax.github.io/bezierinfo/#circles_cubic.
const q = 4 * (math.Sqrt2 - 1) / 3 const q = 4 * (math.Sqrt2 - 1) / 3
@@ -177,9 +172,3 @@ func (e Ellipse) Path(o *op.Ops) PathSpec {
ellipse.shape = ops.Ellipse ellipse.shape = ops.Ellipse
return ellipse return ellipse
} }
func fPt(p image.Point) f32.Point {
return f32.Point{
X: float32(p.X), Y: float32(p.Y),
}
}
+2 -2
View File
@@ -1,17 +1,17 @@
package clip_test package clip_test
import ( import (
"image"
"image/color" "image/color"
"testing" "testing"
"gioui.org/f32"
"gioui.org/op" "gioui.org/op"
"gioui.org/op/clip" "gioui.org/op/clip"
"gioui.org/op/paint" "gioui.org/op/paint"
) )
func TestZeroEllipse(t *testing.T) { func TestZeroEllipse(t *testing.T) {
p := f32.Pt(1.0, 2.0) p := image.Pt(1.0, 2.0)
e := clip.Ellipse{Min: p, Max: p} e := clip.Ellipse{Min: p, Max: p}
ops := new(op.Ops) ops := new(op.Ops)
paint.FillShape(ops, color.NRGBA{R: 255, A: 255}, e.Op(ops)) paint.FillShape(ops, color.NRGBA{R: 255, A: 255}, e.Op(ops))
+7 -7
View File
@@ -3,9 +3,9 @@
package widget package widget
import ( import (
"image"
"image/color" "image/color"
"gioui.org/f32"
"gioui.org/layout" "gioui.org/layout"
"gioui.org/op/clip" "gioui.org/op/clip"
"gioui.org/op/paint" "gioui.org/op/paint"
@@ -21,21 +21,21 @@ type Border struct {
func (b Border) Layout(gtx layout.Context, w layout.Widget) layout.Dimensions { func (b Border) Layout(gtx layout.Context, w layout.Widget) layout.Dimensions {
dims := w(gtx) dims := w(gtx)
sz := layout.FPt(dims.Size) sz := dims.Size
rr := float32(gtx.Px(b.CornerRadius)) rr := gtx.Px(b.CornerRadius)
width := float32(gtx.Px(b.Width)) width := gtx.Px(b.Width)
sz.X -= width sz.X -= width
sz.Y -= width sz.Y -= width
r := f32.Rectangle{Max: sz} r := image.Rectangle{Max: sz}
r = r.Add(f32.Point{X: width * 0.5, Y: width * 0.5}) r = r.Add(image.Point{X: width / 2, Y: width / 2})
paint.FillShape(gtx.Ops, paint.FillShape(gtx.Ops,
b.Color, b.Color,
clip.Stroke{ clip.Stroke{
Path: clip.UniformRRect(r, rr).Path(gtx.Ops), Path: clip.UniformRRect(r, rr).Path(gtx.Ops),
Width: width, Width: float32(width),
}.Op(), }.Op(),
) )
+14 -23
View File
@@ -7,7 +7,6 @@ import (
"image/color" "image/color"
"math" "math"
"gioui.org/f32"
"gioui.org/internal/f32color" "gioui.org/internal/f32color"
"gioui.org/io/semantic" "gioui.org/io/semantic"
"gioui.org/layout" "gioui.org/layout"
@@ -130,11 +129,8 @@ func (b ButtonLayoutStyle) Layout(gtx layout.Context, w layout.Widget) layout.Di
semantic.Button.Add(gtx.Ops) semantic.Button.Add(gtx.Ops)
return layout.Stack{Alignment: layout.Center}.Layout(gtx, return layout.Stack{Alignment: layout.Center}.Layout(gtx,
layout.Expanded(func(gtx layout.Context) layout.Dimensions { layout.Expanded(func(gtx layout.Context) layout.Dimensions {
rr := float32(gtx.Px(b.CornerRadius)) rr := gtx.Px(b.CornerRadius)
defer clip.UniformRRect(f32.Rectangle{Max: f32.Point{ defer clip.UniformRRect(image.Rectangle{Max: gtx.Constraints.Min}, rr).Push(gtx.Ops).Pop()
X: float32(gtx.Constraints.Min.X),
Y: float32(gtx.Constraints.Min.Y),
}}, rr).Push(gtx.Ops).Pop()
background := b.Background background := b.Background
switch { switch {
case gtx.Queue == nil: case gtx.Queue == nil:
@@ -165,12 +161,8 @@ func (b IconButtonStyle) Layout(gtx layout.Context) layout.Dimensions {
} }
return layout.Stack{Alignment: layout.Center}.Layout(gtx, return layout.Stack{Alignment: layout.Center}.Layout(gtx,
layout.Expanded(func(gtx layout.Context) layout.Dimensions { layout.Expanded(func(gtx layout.Context) layout.Dimensions {
sizex, sizey := gtx.Constraints.Min.X, gtx.Constraints.Min.Y rr := (gtx.Constraints.Min.X + gtx.Constraints.Min.Y) / 4
sizexf, sizeyf := float32(sizex), float32(sizey) defer clip.UniformRRect(image.Rectangle{Max: gtx.Constraints.Min}, rr).Push(gtx.Ops).Pop()
rr := (sizexf + sizeyf) * .25
defer clip.UniformRRect(f32.Rectangle{
Max: f32.Point{X: sizexf, Y: sizeyf},
}, rr).Push(gtx.Ops).Pop()
background := b.Background background := b.Background
switch { switch {
case gtx.Queue == nil: case gtx.Queue == nil:
@@ -199,7 +191,7 @@ func (b IconButtonStyle) Layout(gtx layout.Context) layout.Dimensions {
) )
}) })
c := m.Stop() c := m.Stop()
bounds := f32.Rectangle{Max: layout.FPt(dims.Size)} bounds := image.Rectangle{Max: dims.Size}
defer clip.Ellipse(bounds).Push(gtx.Ops).Pop() defer clip.Ellipse(bounds).Push(gtx.Ops).Pop()
c.Add(gtx.Ops) c.Add(gtx.Ops)
return dims return dims
@@ -282,25 +274,24 @@ func drawInk(gtx layout.Context, c widget.Press) {
// Beziér ease-in curve. // Beziér ease-in curve.
alphaBezier := t2 * t2 * (3.0 - 2.0*t2) alphaBezier := t2 * t2 * (3.0 - 2.0*t2)
sizeBezier := sizet * sizet * (3.0 - 2.0*sizet) sizeBezier := sizet * sizet * (3.0 - 2.0*sizet)
size := float32(gtx.Constraints.Min.X) size := gtx.Constraints.Min.X
if h := float32(gtx.Constraints.Min.Y); h > size { if h := gtx.Constraints.Min.Y; h > size {
size = h size = h
} }
// Cover the entire constraints min rectangle. // Cover the entire constraints min rectangle and
size *= 2 * float32(math.Sqrt(2)) // apply curve values to size and color.
// Apply curve values to size and color. size = int(float32(size) * 2 * float32(math.Sqrt(2)) * sizeBezier)
size *= sizeBezier
alpha := 0.7 * alphaBezier alpha := 0.7 * alphaBezier
const col = 0.8 const col = 0.8
ba, bc := byte(alpha*0xff), byte(col*0xff) ba, bc := byte(alpha*0xff), byte(col*0xff)
rgba := f32color.MulAlpha(color.NRGBA{A: 0xff, R: bc, G: bc, B: bc}, ba) rgba := f32color.MulAlpha(color.NRGBA{A: 0xff, R: bc, G: bc, B: bc}, ba)
ink := paint.ColorOp{Color: rgba} ink := paint.ColorOp{Color: rgba}
ink.Add(gtx.Ops) ink.Add(gtx.Ops)
rr := size * .5 rr := size / 2
defer op.Offset(c.Position.Add(image.Point{ defer op.Offset(c.Position.Add(image.Point{
X: -int(rr), X: -rr,
Y: -int(rr), Y: -rr,
})).Push(gtx.Ops).Pop() })).Push(gtx.Ops).Pop()
defer clip.UniformRRect(f32.Rectangle{Max: f32.Pt(size, size)}, rr).Push(gtx.Ops).Pop() defer clip.UniformRRect(image.Rectangle{Max: image.Pt(size, size)}, rr).Push(gtx.Ops).Pop()
paint.PaintOp{}.Add(gtx.Ops) paint.PaintOp{}.Add(gtx.Ops)
} }
+1 -2
View File
@@ -6,7 +6,6 @@ import (
"image" "image"
"image/color" "image/color"
"gioui.org/f32"
"gioui.org/internal/f32color" "gioui.org/internal/f32color"
"gioui.org/layout" "gioui.org/layout"
"gioui.org/op/clip" "gioui.org/op/clip"
@@ -50,7 +49,7 @@ func (c *checkable) layout(gtx layout.Context, checked, hovered bool) layout.Dim
background := f32color.MulAlpha(c.IconColor, 70) background := f32color.MulAlpha(c.IconColor, 70)
b := f32.Rectangle{Max: f32.Pt(float32(size), float32(size))} b := image.Rectangle{Max: image.Pt(size, size)}
paint.FillShape(gtx.Ops, background, clip.Ellipse(b).Op(gtx.Ops)) paint.FillShape(gtx.Ops, background, clip.Ellipse(b).Op(gtx.Ops))
return dims return dims
+11 -13
View File
@@ -144,19 +144,18 @@ func minimizeWindow(gtx layout.Context) layout.Dimensions {
// maximizeWindow draws a rectangle representing the maximize action. // maximizeWindow draws a rectangle representing the maximize action.
func maximizeWindow(gtx layout.Context) layout.Dimensions { func maximizeWindow(gtx layout.Context) layout.Dimensions {
size := gtx.Px(winIconSize) size := gtx.Px(winIconSize)
size32 := float32(size) margin := gtx.Px(winIconMargin)
margin := float32(gtx.Px(winIconMargin)) width := gtx.Px(winIconStroke)
width := float32(gtx.Px(winIconStroke))
r := clip.RRect{ r := clip.RRect{
Rect: f32.Rect(margin, margin, size32-margin, size32-margin), Rect: image.Rect(margin, margin, size-margin, size-margin),
} }
st := clip.Stroke{ st := clip.Stroke{
Path: r.Path(gtx.Ops), Path: r.Path(gtx.Ops),
Width: width, Width: float32(width),
}.Op().Push(gtx.Ops) }.Op().Push(gtx.Ops)
paint.PaintOp{}.Add(gtx.Ops) paint.PaintOp{}.Add(gtx.Ops)
st.Pop() st.Pop()
r.Rect.Max = f32.Pt(size32-margin, 2*margin) r.Rect.Max = image.Pt(size-margin, 2*margin)
st = clip.Outline{ st = clip.Outline{
Path: r.Path(gtx.Ops), Path: r.Path(gtx.Ops),
}.Op().Push(gtx.Ops) }.Op().Push(gtx.Ops)
@@ -168,24 +167,23 @@ func maximizeWindow(gtx layout.Context) layout.Dimensions {
// maximizedWindow draws interleaved rectangles representing the un-maximize action. // maximizedWindow draws interleaved rectangles representing the un-maximize action.
func maximizedWindow(gtx layout.Context) layout.Dimensions { func maximizedWindow(gtx layout.Context) layout.Dimensions {
size := gtx.Px(winIconSize) size := gtx.Px(winIconSize)
size32 := float32(size) margin := gtx.Px(winIconMargin)
margin := float32(gtx.Px(winIconMargin)) width := gtx.Px(winIconStroke)
width := float32(gtx.Px(winIconStroke))
r := clip.RRect{ r := clip.RRect{
Rect: f32.Rect(margin, margin, size32-2*margin, size32-2*margin), Rect: image.Rect(margin, margin, size-2*margin, size-2*margin),
} }
st := clip.Stroke{ st := clip.Stroke{
Path: r.Path(gtx.Ops), Path: r.Path(gtx.Ops),
Width: width, Width: float32(width),
}.Op().Push(gtx.Ops) }.Op().Push(gtx.Ops)
paint.PaintOp{}.Add(gtx.Ops) paint.PaintOp{}.Add(gtx.Ops)
st.Pop() st.Pop()
r = clip.RRect{ r = clip.RRect{
Rect: f32.Rect(2*margin, 2*margin, size32-margin, size32-margin), Rect: image.Rect(2*margin, 2*margin, size-margin, size-margin),
} }
st = clip.Stroke{ st = clip.Stroke{
Path: r.Path(gtx.Ops), Path: r.Path(gtx.Ops),
Width: width, Width: float32(width),
}.Op().Push(gtx.Ops) }.Op().Push(gtx.Ops)
paint.PaintOp{}.Add(gtx.Ops) paint.PaintOp{}.Add(gtx.Ops)
st.Pop() st.Pop()
+3 -5
View File
@@ -7,7 +7,6 @@ import (
"image/color" "image/color"
"math" "math"
"gioui.org/f32"
"gioui.org/io/pointer" "gioui.org/io/pointer"
"gioui.org/layout" "gioui.org/layout"
"gioui.org/op" "gioui.org/op"
@@ -188,15 +187,14 @@ func (s ScrollbarStyle) layout(gtx layout.Context, axis layout.Axis, viewportSta
X: indicatorLen, X: indicatorLen,
Y: gtx.Px(s.Indicator.MinorWidth), Y: gtx.Px(s.Indicator.MinorWidth),
}) })
indicatorDimsF := layout.FPt(indicatorDims) radius := gtx.Px(s.Indicator.CornerRadius)
radius := float32(gtx.Px(s.Indicator.CornerRadius))
// Lay out the indicator. // Lay out the indicator.
offset := axis.Convert(image.Pt(viewStart, 0)) offset := axis.Convert(image.Pt(viewStart, 0))
defer op.Offset(offset).Push(gtx.Ops).Pop() defer op.Offset(offset).Push(gtx.Ops).Pop()
paint.FillShape(gtx.Ops, s.Indicator.Color, clip.RRect{ paint.FillShape(gtx.Ops, s.Indicator.Color, clip.RRect{
Rect: f32.Rectangle{ Rect: image.Rectangle{
Max: indicatorDimsF, Max: indicatorDims,
}, },
SW: radius, SW: radius,
NW: radius, NW: radius,
+7 -9
View File
@@ -6,7 +6,6 @@ import (
"image" "image"
"image/color" "image/color"
"gioui.org/f32"
"gioui.org/internal/f32color" "gioui.org/internal/f32color"
"gioui.org/layout" "gioui.org/layout"
"gioui.org/op/clip" "gioui.org/op/clip"
@@ -29,27 +28,26 @@ func ProgressBar(th *Theme, progress float32) ProgressBarStyle {
} }
func (p ProgressBarStyle) Layout(gtx layout.Context) layout.Dimensions { func (p ProgressBarStyle) Layout(gtx layout.Context) layout.Dimensions {
shader := func(width float32, color color.NRGBA) layout.Dimensions { shader := func(width int, color color.NRGBA) layout.Dimensions {
maxHeight := unit.Dp(4) var maxHeight = unit.Dp(4)
rr := float32(gtx.Px(unit.Dp(2))) rr := gtx.Px(unit.Dp(2))
d := image.Point{X: int(width), Y: gtx.Px(maxHeight)} d := image.Point{X: width, Y: gtx.Px(maxHeight)}
height := float32(gtx.Px(maxHeight)) defer clip.UniformRRect(image.Rectangle{Max: image.Pt(width, d.Y)}, rr).Push(gtx.Ops).Pop()
defer clip.UniformRRect(f32.Rectangle{Max: f32.Pt(width, height)}, rr).Push(gtx.Ops).Pop()
paint.ColorOp{Color: color}.Add(gtx.Ops) paint.ColorOp{Color: color}.Add(gtx.Ops)
paint.PaintOp{}.Add(gtx.Ops) paint.PaintOp{}.Add(gtx.Ops)
return layout.Dimensions{Size: d} return layout.Dimensions{Size: d}
} }
progressBarWidth := float32(gtx.Constraints.Max.X) progressBarWidth := gtx.Constraints.Max.X
return layout.Stack{Alignment: layout.W}.Layout(gtx, return layout.Stack{Alignment: layout.W}.Layout(gtx,
layout.Stacked(func(gtx layout.Context) layout.Dimensions { layout.Stacked(func(gtx layout.Context) layout.Dimensions {
return shader(progressBarWidth, p.TrackColor) return shader(progressBarWidth, p.TrackColor)
}), }),
layout.Stacked(func(gtx layout.Context) layout.Dimensions { layout.Stacked(func(gtx layout.Context) layout.Dimensions {
fillWidth := progressBarWidth * clamp1(p.Progress) fillWidth := int(float32(progressBarWidth) * clamp1(p.Progress))
fillColor := p.Color fillColor := p.Color
if gtx.Queue == nil { if gtx.Queue == nil {
fillColor = f32color.Disabled(fillColor) fillColor = f32color.Disabled(fillColor)
+3 -4
View File
@@ -6,7 +6,6 @@ import (
"image" "image"
"image/color" "image/color"
"gioui.org/f32"
"gioui.org/internal/f32color" "gioui.org/internal/f32color"
"gioui.org/layout" "gioui.org/layout"
"gioui.org/op" "gioui.org/op"
@@ -78,9 +77,9 @@ func (s SliderStyle) Layout(gtx layout.Context) layout.Dimensions {
// Draw thumb. // Draw thumb.
pt := axis.Convert(image.Pt(thumbPos, sizeCross/2)) pt := axis.Convert(image.Pt(thumbPos, sizeCross/2))
thumb := f32.Rectangle{ thumb := image.Rectangle{
Min: f32.Pt(float32(pt.X-thumbRadius), float32(pt.Y-thumbRadius)), Min: image.Pt(pt.X-thumbRadius, pt.Y-thumbRadius),
Max: f32.Pt(float32(pt.X+thumbRadius), float32(pt.Y+thumbRadius)), Max: image.Pt(pt.X+thumbRadius, pt.Y+thumbRadius),
} }
paint.FillShape(gtx.Ops, color, clip.Ellipse(thumb).Op(gtx.Ops)) paint.FillShape(gtx.Ops, color, clip.Ellipse(thumb).Op(gtx.Ops))
+16 -17
View File
@@ -6,7 +6,6 @@ import (
"image" "image"
"image/color" "image/color"
"gioui.org/f32"
"gioui.org/internal/f32color" "gioui.org/internal/f32color"
"gioui.org/io/semantic" "gioui.org/io/semantic"
"gioui.org/layout" "gioui.org/layout"
@@ -47,10 +46,10 @@ func (s SwitchStyle) Layout(gtx layout.Context) layout.Dimensions {
trackOff := (thumbSize - trackHeight) / 2 trackOff := (thumbSize - trackHeight) / 2
// Draw track. // Draw track.
trackCorner := float32(trackHeight) / 2 trackCorner := trackHeight / 2
trackRect := f32.Rectangle{Max: f32.Point{ trackRect := image.Rectangle{Max: image.Point{
X: float32(trackWidth), X: trackWidth,
Y: float32(trackHeight), Y: trackHeight,
}} }}
col := s.Color.Disabled col := s.Color.Disabled
if s.Switch.Value { if s.Switch.Value {
@@ -69,14 +68,14 @@ func (s SwitchStyle) Layout(gtx layout.Context) layout.Dimensions {
// Draw thumb ink. // Draw thumb ink.
inkSize := gtx.Px(unit.Dp(44)) inkSize := gtx.Px(unit.Dp(44))
rr := float32(inkSize) * .5 rr := inkSize / 2
inkOff := image.Point{ inkOff := image.Point{
X: trackWidth/2 - int(rr), X: trackWidth/2 - rr,
Y: -int(rr) + trackHeight/2 + trackOff, Y: -rr + trackHeight/2 + trackOff,
} }
t = op.Offset(inkOff).Push(gtx.Ops) t = op.Offset(inkOff).Push(gtx.Ops)
gtx.Constraints.Min = image.Pt(inkSize, inkSize) gtx.Constraints.Min = image.Pt(inkSize, inkSize)
cl = clip.UniformRRect(f32.Rectangle{Max: layout.FPt(gtx.Constraints.Min)}, rr).Push(gtx.Ops) cl = clip.UniformRRect(image.Rectangle{Max: gtx.Constraints.Min}, rr).Push(gtx.Ops)
for _, p := range s.Switch.History() { for _, p := range s.Switch.History() {
drawInk(gtx, p) drawInk(gtx, p)
} }
@@ -89,18 +88,18 @@ func (s SwitchStyle) Layout(gtx layout.Context) layout.Dimensions {
defer op.Offset(image.Point{X: xoff}).Push(gtx.Ops).Pop() defer op.Offset(image.Point{X: xoff}).Push(gtx.Ops).Pop()
} }
thumbRadius := float32(thumbSize) / 2 thumbRadius := thumbSize / 2
circle := func(x, y, r float32) clip.Op { circle := func(x, y, r int) clip.Op {
b := f32.Rectangle{ b := image.Rectangle{
Min: f32.Pt(x-r, y-r), Min: image.Pt(x-r, y-r),
Max: f32.Pt(x+r, y+r), Max: image.Pt(x+r, y+r),
} }
return clip.Ellipse(b).Op(gtx.Ops) return clip.Ellipse(b).Op(gtx.Ops)
} }
// Draw hover. // Draw hover.
if s.Switch.Hovered() || s.Switch.Focused() { if s.Switch.Hovered() || s.Switch.Focused() {
r := 1.7 * thumbRadius r := thumbRadius * 10 / 17
background := f32color.MulAlpha(s.Color.Enabled, 70) background := f32color.MulAlpha(s.Color.Enabled, 70)
paint.FillShape(gtx.Ops, background, circle(thumbRadius, thumbRadius, r)) paint.FillShape(gtx.Ops, background, circle(thumbRadius, thumbRadius, r))
} }
@@ -108,7 +107,7 @@ func (s SwitchStyle) Layout(gtx layout.Context) layout.Dimensions {
// Draw thumb shadow, a translucent disc slightly larger than the // Draw thumb shadow, a translucent disc slightly larger than the
// thumb itself. // thumb itself.
// Center shadow horizontally and slightly adjust its Y. // Center shadow horizontally and slightly adjust its Y.
paint.FillShape(gtx.Ops, argb(0x55000000), circle(thumbRadius, thumbRadius+.25, thumbRadius+1)) paint.FillShape(gtx.Ops, argb(0x55000000), circle(thumbRadius, thumbRadius+gtx.Px(unit.Dp(.25)), thumbRadius+1))
// Draw thumb. // Draw thumb.
paint.FillShape(gtx.Ops, col, circle(thumbRadius, thumbRadius, thumbRadius)) paint.FillShape(gtx.Ops, col, circle(thumbRadius, thumbRadius, thumbRadius))
@@ -121,7 +120,7 @@ func (s SwitchStyle) Layout(gtx layout.Context) layout.Dimensions {
} }
defer op.Offset(clickOff).Push(gtx.Ops).Pop() defer op.Offset(clickOff).Push(gtx.Ops).Pop()
sz := image.Pt(clickSize, clickSize) sz := image.Pt(clickSize, clickSize)
defer clip.Ellipse(f32.Rectangle{Max: layout.FPt(sz)}).Push(gtx.Ops).Pop() defer clip.Ellipse(image.Rectangle{Max: sz}).Push(gtx.Ops).Pop()
s.Switch.Layout(gtx, func(gtx layout.Context) layout.Dimensions { s.Switch.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
if d := s.Description; d != "" { if d := s.Description; d != "" {
semantic.DescriptionOp(d).Add(gtx.Ops) semantic.DescriptionOp(d).Add(gtx.Ops)