diff --git a/app/os_macos.go b/app/os_macos.go index a8df66b2..10734e8f 100644 --- a/app/os_macos.go +++ b/app/os_macos.go @@ -893,7 +893,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). scale := 1. / float32(C.getViewBackingScale(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) bounds := f32.Rectangle{ Min: t.Transform(sel.Pos.Sub(f32.Pt(0, sel.Ascent))), diff --git a/f32/affine.go b/f32/affine.go index 6ac00d59..4934e4e1 100644 --- a/f32/affine.go +++ b/f32/affine.go @@ -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. func (a Affine2D) Offset(offset Point) Affine2D { return Affine2D{ diff --git a/f32/affine_test.go b/f32/affine_test.go index b572d237..010a3144 100644 --- a/f32/affine_test.go +++ b/f32/affine_test.go @@ -51,6 +51,9 @@ func TestString(t *testing.T) { }, { 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]]", + }, { + in: AffineId(), + exp: "[[1 0 0] [0 1 0]]", }, } for _, test := range tests { diff --git a/gpu/gpu.go b/gpu/gpu.go index 3ea528af..4b8f674d 100644 --- a/gpu/gpu.go +++ b/gpu/gpu.go @@ -872,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]) sr := f32.FRect(v) 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. ops[l.opStart] = imageOp{ clip: l.clip, @@ -1196,7 +1196,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.Max.Y -= float32(dr.Max.Y-clip.Max.Y) * sdy / dy 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 } return m @@ -1371,7 +1371,7 @@ func gradientSpaceTransform(clip image.Rectangle, off f32.Point, stop1, stop2 f3 // TODO: optimize zp := f32.Point{} - return f32.Affine2D{}. + return f32.AffineId(). 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(stop1)). // offset to first stop point diff --git a/gpu/internal/rendertest/bench_test.go b/gpu/internal/rendertest/bench_test.go index f54732ca..5e4562df 100644 --- a/gpu/internal/rendertest/bench_test.go +++ b/gpu/internal/rendertest/bench_test.go @@ -88,7 +88,7 @@ func BenchmarkDrawUI(b *testing.B) { resetOps(gtx) 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) @@ -110,7 +110,7 @@ func BenchmarkDrawUITransformed(b *testing.B) { resetOps(gtx) 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) drawCore(gtx, th) diff --git a/gpu/internal/rendertest/clip_test.go b/gpu/internal/rendertest/clip_test.go index 37701e19..152fc7cd 100644 --- a/gpu/internal/rendertest/clip_test.go +++ b/gpu/internal/rendertest/clip_test.go @@ -299,7 +299,7 @@ func TestInstancedRects(t *testing.T) { c := macro.Stop() for range 2 { - op.Affine(f32.Affine2D{}.Rotate(f32.Pt(0, 0), .2)).Add(o) + op.Affine(f32.AffineId().Rotate(f32.Pt(0, 0), .2)).Add(o) c.Add(o) op.Offset(image.Pt(20, 20)).Add(o) } diff --git a/gpu/internal/rendertest/render_test.go b/gpu/internal/rendertest/render_test.go index 4c60f658..18b9ad65 100644 --- a/gpu/internal/rendertest/render_test.go +++ b/gpu/internal/rendertest/render_test.go @@ -87,10 +87,10 @@ func TestNoClipFromPaint(t *testing.T) { // ensure that a paint operation does not pollute the state // by leaving any clip paths in place. 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() 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() paint.FillShape(o, black, clip.Rect(image.Rect(0, 0, 50, 50)).Op()) @@ -109,7 +109,7 @@ func TestDeferredPaint(t *testing.T) { paint.PaintOp{}.Add(o) 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) 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) @@ -119,7 +119,7 @@ func TestDeferredPaint(t *testing.T) { op.Defer(o, paintMacro) 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() paint.ColorOp{Color: color.NRGBA{A: 0x60, B: 0xff}}.Add(o) paint.PaintOp{}.Add(o) @@ -260,7 +260,7 @@ func TestLinearGradient(t *testing.T) { Color2: g.To, }.Add(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) paint.PaintOp{}.Add(ops) t2.Pop() @@ -363,7 +363,7 @@ func TestImageRGBA_ScaleLinear(t *testing.T) { run(t, func(o *op.Ops) { w := newWindow(t, 128, 128) 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.Set(0, 0, colornames.Red) @@ -397,7 +397,7 @@ func TestImageRGBA_ScaleLinear(t *testing.T) { func TestImageRGBA_ScaleNearest(t *testing.T) { run(t, func(o *op.Ops) { 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.Set(0, 0, colornames.Red) diff --git a/gpu/internal/rendertest/transform_test.go b/gpu/internal/rendertest/transform_test.go index 060fa42a..78852ec8 100644 --- a/gpu/internal/rendertest/transform_test.go +++ b/gpu/internal/rendertest/transform_test.go @@ -29,7 +29,7 @@ func TestPaintOffset(t *testing.T) { func TestPaintRotate(t *testing.T) { 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() paint.FillShape(o, red, clip.Rect(image.Rect(20, 20, 60, 60)).Op()) }, func(r result) { @@ -42,7 +42,7 @@ func TestPaintRotate(t *testing.T) { func TestPaintShear(t *testing.T) { 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() paint.FillShape(o, red, clip.Rect(image.Rect(0, 0, 40, 40)).Op()) }, func(r result) { @@ -79,7 +79,7 @@ func TestClipOffset(t *testing.T) { 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)) + a := f32.AffineId().Scale(f32.Point{}, f32.Pt(2, 2)).Offset(f32.Pt(10, 10)) defer op.Affine(a).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()) @@ -93,7 +93,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 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() paint.FillShape(o, red, clip.Rect(image.Rect(0, 40, 100, 100)).Op()) }, func(r result) { @@ -121,7 +121,7 @@ func TestOffsetScaleTexture(t *testing.T) { run(t, func(o *op.Ops) { defer op.Offset(image.Pt(15, 15)).Push(o).Pop() 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() paint.PaintOp{}.Add(o) }, func(r result) { @@ -133,7 +133,7 @@ func TestOffsetScaleTexture(t *testing.T) { func TestRotateTexture(t *testing.T) { run(t, func(o *op.Ops) { 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 scale(20.0/512, 20.0/512).Push(o).Pop() paint.PaintOp{}.Add(o) @@ -146,10 +146,10 @@ func TestRotateTexture(t *testing.T) { func TestRotateClipTexture(t *testing.T) { run(t, func(o *op.Ops) { 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 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() paint.PaintOp{}.Add(o) }, 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() - 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 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) { // check the ordering of operations bot in affine and in gpu stack. 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() - 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() - 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() paint.FillShape(o, red, clip.Rect(image.Rect(0, 0, 20, 20)).Op()) }, func(r result) { diff --git a/gpu/internal/rendertest/util_test.go b/gpu/internal/rendertest/util_test.go index a1388163..e11c5947 100644 --- a/gpu/internal/rendertest/util_test.go +++ b/gpu/internal/rendertest/util_test.go @@ -302,5 +302,5 @@ func newWindow(t testing.TB, width, height int) *headless.Window { } 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))) } diff --git a/internal/f32/f32.go b/internal/f32/f32.go index 771b0988..f1da21df 100644 --- a/internal/f32/f32.go +++ b/internal/f32/f32.go @@ -19,6 +19,8 @@ type Affine2D = f32.Affine2D var NewAffine2D = f32.NewAffine2D +var AffineId = f32.AffineId + // A Rectangle contains the points (X, Y) where Min.X <= X < Max.X, // Min.Y <= Y < Max.Y. type Rectangle struct { diff --git a/op/op.go b/op/op.go index e4af8f6c..33ff3676 100644 --- a/op/op.go +++ b/op/op.go @@ -183,7 +183,7 @@ func (c CallOp) Add(o *Ops) { // 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.Affine2D{}.Offset(offf)) + return Affine(f32.AffineId().Offset(offf)) } // Affine creates a TransformOp representing the transformation a. diff --git a/text/gotext.go b/text/gotext.go index be3f84d8..d7b35aee 100644 --- a/text/gotext.go +++ b/text/gotext.go @@ -767,7 +767,7 @@ func (s *shaperImpl) Bitmaps(ops *op.Ops, gs []Glyph) op.CallOp { imgOp = bitmapData.img 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), Y: fixedToFloat(g.Offset.Y + g.Bounds.Min.Y), })).Push(ops) @@ -783,7 +783,7 @@ func (s *shaperImpl) Bitmaps(ops *op.Ops, gs []Glyph) op.CallOp { Y: g.Bounds.Max.Y.Round(), }, }.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), Y: float32(glyphSize.Y) / float32(imgSize.Y), })).Push(ops) diff --git a/widget/fit.go b/widget/fit.go index 366704be..4b8599ea 100644 --- a/widget/fit.go +++ b/widget/fit.go @@ -39,7 +39,7 @@ func (fit Fit) scale(cs layout.Constraints, pos layout.Direction, dims layout.Di offset := pos.Position(widgetSize, dims.Size) dims.Baseline += offset.Y - return dims, f32.Affine2D{}.Offset(layout.FPt(offset)) + return dims, f32.AffineId().Offset(layout.FPt(offset)) } 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) dims.Baseline += offset.Y - return dims, f32.Affine2D{}.Offset(layout.FPt(offset)) + return dims, f32.AffineId().Offset(layout.FPt(offset)) } 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) offset := pos.Position(scaledSize, dims.Size) - trans := f32.Affine2D{}. + trans := f32.AffineId(). Scale(f32.Point{}, scale). Offset(layout.FPt(offset)) diff --git a/widget/image.go b/widget/image.go index 5cc6f6b0..b9442956 100644 --- a/widget/image.go +++ b/widget/image.go @@ -44,7 +44,7 @@ func (im Image) Layout(gtx layout.Context) layout.Dimensions { defer clip.Rect{Max: dims.Size}.Push(gtx.Ops).Pop() 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() im.Src.Add(gtx.Ops) diff --git a/widget/label.go b/widget/label.go index 8c375709..738f8ce2 100644 --- a/widget/label.go +++ b/widget/label.go @@ -210,7 +210,7 @@ func (it *textIterator) paintGlyph(gtx layout.Context, shaper *text.Shaper, glyp line = append(line, glyph) } 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) outline := clip.Outline{Path: path}.Op().Push(gtx.Ops) it.material.Add(gtx.Ops)