Skip to content

Commit

Permalink
all: [API] change clip.RRect and UniformRRect to take integer coordin…
Browse files Browse the repository at this point in the history
…ates

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>
  • Loading branch information
eliasnaur committed May 31, 2022
1 parent a63e0cb commit 48a8540
Show file tree
Hide file tree
Showing 16 changed files with 103 additions and 139 deletions.
15 changes: 4 additions & 11 deletions gpu/headless/headless_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"image/color"
"testing"

"gioui.org/f32"
"gioui.org/internal/f32color"
"gioui.org/op"
"gioui.org/op/clip"
Expand Down Expand Up @@ -50,20 +49,14 @@ func TestClipping(t *testing.T) {
var ops op.Ops
paint.ColorOp{Color: col}.Add(&ops)
clip.RRect{
Rect: f32.Rectangle{
Min: f32.Point{X: 50, Y: 50},
Max: f32.Point{X: 250, Y: 250},
},
SE: 75,
Rect: image.Rect(50, 50, 250, 250),
SE: 75,
}.Push(&ops)
paint.PaintOp{}.Add(&ops)
paint.ColorOp{Color: col2}.Add(&ops)
clip.RRect{
Rect: f32.Rectangle{
Min: f32.Point{X: 100, Y: 100},
Max: f32.Point{X: 350, Y: 350},
},
NW: 75,
Rect: image.Rect(100, 100, 350, 350),
NW: 75,
}.Push(&ops)
paint.PaintOp{}.Add(&ops)
if err := w.Frame(&ops); err != nil {
Expand Down
8 changes: 4 additions & 4 deletions gpu/internal/rendertest/bench_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ func draw1000Circles(gtx layout.Context) {
for y := 0; y < 10; y++ {
paint.FillShape(ops,
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)
}
Expand All @@ -172,7 +172,7 @@ func draw1000CirclesInstanced(gtx layout.Context) {
ops := gtx.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)
cl.Pop()
c := r.Stop()
Expand Down Expand Up @@ -207,7 +207,7 @@ func drawIndividualShapes(gtx layout.Context, th *material.Theme) chan op.CallOp
for y := 0; y < 9; y++ {
paint.FillShape(ops,
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)
}
Expand All @@ -224,7 +224,7 @@ func drawShapeInstances(gtx layout.Context, th *material.Theme) chan op.CallOp {
co := 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)
cl.Pop()
c := r.Stop()
Expand Down
14 changes: 7 additions & 7 deletions gpu/internal/rendertest/clip_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ func TestPaintRect(t *testing.T) {

func TestPaintClippedRect(t *testing.T) {
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())
}, func(r result) {
r.expect(0, 0, transparent)
Expand All @@ -42,8 +42,8 @@ func TestPaintClippedRect(t *testing.T) {

func TestPaintClippedCircle(t *testing.T) {
run(t, func(o *op.Ops) {
r := float32(10)
defer clip.RRect{Rect: f32.Rect(20, 20, 40, 40), SE: r, SW: r, NW: r, NE: r}.Push(o).Pop()
const r = 10
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()
paint.Fill(o, red)
}, func(r result) {
Expand Down Expand Up @@ -126,10 +126,10 @@ func TestTexturedStrokeClipped(t *testing.T) {
smallSquares.Add(o)
defer op.Offset(image.Pt(50, 50)).Push(o).Pop()
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,
}.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()
paint.PaintOp{}.Add(o)
}, func(r result) {
Expand All @@ -141,7 +141,7 @@ func TestTexturedStroke(t *testing.T) {
smallSquares.Add(o)
defer op.Offset(image.Pt(50, 50)).Push(o).Pop()
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,
}.Op().Push(o).Pop()
defer op.Offset(image.Pt(-10, -10)).Push(o).Pop()
Expand All @@ -153,7 +153,7 @@ func TestTexturedStroke(t *testing.T) {
func TestPaintClippedTexture(t *testing.T) {
run(t, func(o *op.Ops) {
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()
paint.PaintOp{}.Add(o)
}, func(r result) {
Expand Down
6 changes: 3 additions & 3 deletions gpu/internal/rendertest/render_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ func constSqPath() clip.Op {

func constSqCirc() clip.Op {
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)
}

Expand Down Expand Up @@ -204,7 +204,7 @@ func TestBuildOffscreen(t *testing.T) {

func TestNegativeOverlaps(t *testing.T) {
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()
paint.PaintOp{}.Add(ops)
}, func(r result) {
Expand Down Expand Up @@ -257,7 +257,7 @@ func TestLinearGradient(t *testing.T) {
Stop2: f32.Pt(gr.Max.X, gr.Min.Y),
Color2: g.To,
}.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)
t2 := scale(pixelAligned.Dx()/128, 1).Push(ops)
paint.PaintOp{}.Add(ops)
Expand Down
14 changes: 7 additions & 7 deletions gpu/internal/rendertest/transform_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ func TestPaintShear(t *testing.T) {

func TestClipPaintOffset(t *testing.T) {
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()
paint.FillShape(o, red, clip.Rect(image.Rect(0, 0, 100, 100)).Op())
}, func(r result) {
Expand All @@ -66,7 +66,7 @@ func TestClipPaintOffset(t *testing.T) {
func TestClipOffset(t *testing.T) {
run(t, func(o *op.Ops) {
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())
}, func(r result) {
r.expect(0, 0, transparent)
Expand All @@ -81,7 +81,7 @@ func TestClipScale(t *testing.T) {
run(t, func(o *op.Ops) {
a := f32.Affine2D{}.Scale(f32.Point{}, f32.Pt(2, 2)).Offset(f32.Pt(10, 10))
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())
}, func(r result) {
r.expect(19+10, 19+10, transparent)
Expand All @@ -94,7 +94,7 @@ func TestClipScale(t *testing.T) {
func TestClipRotate(t *testing.T) {
run(t, func(o *op.Ops) {
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())
}, func(r result) {
r.expect(39, 39, transparent)
Expand Down Expand Up @@ -148,7 +148,7 @@ func TestRotateClipTexture(t *testing.T) {
squares.Add(o)
a := f32.Affine2D{}.Rotate(f32.Pt(40, 40), math.Pi/8)
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 scale(60.0/512, 60.0/512).Push(o).Pop()
paint.PaintOp{}.Add(o)
Expand All @@ -166,11 +166,11 @@ func TestComplicatedTransform(t *testing.T) {
run(t, func(o *op.Ops) {
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)
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()
paint.PaintOp{}.Add(o)
Expand Down
2 changes: 1 addition & 1 deletion io/router/pointer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -741,7 +741,7 @@ func TestEllipse(t *testing.T) {
var ops op.Ops

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)
cl.Pop()
var r Router
Expand Down
37 changes: 13 additions & 24 deletions op/clip/shapes.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ func (r Rect) Path() PathSpec {

// UniformRRect returns an RRect with all corner radii set to the
// provided radius.
func UniformRRect(rect f32.Rectangle, radius float32) RRect {
func UniformRRect(rect image.Rectangle, radius int) RRect {
return RRect{
Rect: rect,
SE: radius,
Expand All @@ -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
// construct a circular clip area.
type RRect struct {
Rect f32.Rectangle
Rect image.Rectangle
// The corner radii.
SE, SW, NW, NE float32
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 {
r := image.Rectangle{
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 Rect(rr.Rect).Op()
}
return Outline{Path: rr.Path(ops)}.Op()
}
Expand All @@ -87,8 +80,9 @@ func (rr RRect) Path(ops *op.Ops) PathSpec {
const q = 4 * (math.Sqrt2 - 1) / 3
const iq = 1 - q

se, sw, nw, ne := rr.SE, rr.SW, rr.NW, rr.NE
w, n, e, s := rr.Rect.Min.X, rr.Rect.Min.Y, rr.Rect.Max.X, rr.Rect.Max.Y
se, sw, nw, ne := float32(rr.SE), float32(rr.SW), float32(rr.NW), float32(rr.NE)
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.LineTo(f32.Point{X: e - ne, Y: n}) // N
Expand Down Expand Up @@ -117,7 +111,7 @@ func (rr RRect) Path(ops *op.Ops) PathSpec {

// Ellipse represents the largest axis-aligned ellipse that
// is contained in its bounds.
type Ellipse f32.Rectangle
type Ellipse image.Rectangle

// Op returns the op for the filled ellipse.
func (e Ellipse) Op(ops *op.Ops) Op {
Expand All @@ -131,20 +125,21 @@ func (e Ellipse) Push(ops *op.Ops) Stack {

// Path constructs a path for the ellipse.
func (e Ellipse) Path(o *op.Ops) PathSpec {
bounds := f32.Rectangle(e)
bounds := image.Rectangle(e)
if bounds.Dx() == 0 || bounds.Dy() == 0 {
return PathSpec{shape: ops.Rect}
}

var p Path
p.Begin(o)

center := bounds.Max.Add(bounds.Min).Mul(.5)
diam := bounds.Dx()
bf := 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 := bounds.Dy() / diam
scale := bf.Dy() / diam

// https://pomax.github.io/bezierinfo/#circles_cubic.
const q = 4 * (math.Sqrt2 - 1) / 3
Expand Down Expand Up @@ -177,9 +172,3 @@ func (e Ellipse) Path(o *op.Ops) PathSpec {
ellipse.shape = ops.Ellipse
return ellipse
}

func fPt(p image.Point) f32.Point {
return f32.Point{
X: float32(p.X), Y: float32(p.Y),
}
}
4 changes: 2 additions & 2 deletions op/clip/shapes_test.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
package clip_test

import (
"image"
"image/color"
"testing"

"gioui.org/f32"
"gioui.org/op"
"gioui.org/op/clip"
"gioui.org/op/paint"
)

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}
ops := new(op.Ops)
paint.FillShape(ops, color.NRGBA{R: 255, A: 255}, e.Op(ops))
Expand Down
14 changes: 7 additions & 7 deletions widget/border.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
package widget

import (
"image"
"image/color"

"gioui.org/f32"
"gioui.org/layout"
"gioui.org/op/clip"
"gioui.org/op/paint"
Expand All @@ -21,21 +21,21 @@ type Border struct {

func (b Border) Layout(gtx layout.Context, w layout.Widget) layout.Dimensions {
dims := w(gtx)
sz := layout.FPt(dims.Size)
sz := dims.Size

rr := float32(gtx.Px(b.CornerRadius))
width := float32(gtx.Px(b.Width))
rr := gtx.Px(b.CornerRadius)
width := gtx.Px(b.Width)
sz.X -= width
sz.Y -= width

r := f32.Rectangle{Max: sz}
r = r.Add(f32.Point{X: width * 0.5, Y: width * 0.5})
r := image.Rectangle{Max: sz}
r = r.Add(image.Point{X: width / 2, Y: width / 2})

paint.FillShape(gtx.Ops,
b.Color,
clip.Stroke{
Path: clip.UniformRRect(r, rr).Path(gtx.Ops),
Width: width,
Width: float32(width),
}.Op(),
)

Expand Down

0 comments on commit 48a8540

Please sign in to comment.