aboutsummaryrefslogtreecommitdiffstats
path: root/style/style_test.go
diff options
context:
space:
mode:
authorSam Anthony <sam@samanthony.xyz>2026-03-03 14:22:43 -0500
committerSam Anthony <sam@samanthony.xyz>2026-03-03 14:22:43 -0500
commit0a0b9b8cc9cdc0ffe1819de0266dd1e3c3eb564f (patch)
treeead2723b26a2dcb1d1db80efc01390579056d4fc /style/style_test.go
parent8858a54b5ddb3a2d8a42ecb1a837c02800bc934f (diff)
downloadgui-0a0b9b8cc9cdc0ffe1819de0266dd1e3c3eb564f.zip
style: unit conversion
Implemented the golang.org/x/exp/shiny/unit.Converter interface on style.Style.
Diffstat (limited to 'style/style_test.go')
-rw-r--r--style/style_test.go202
1 files changed, 202 insertions, 0 deletions
diff --git a/style/style_test.go b/style/style_test.go
new file mode 100644
index 0000000..14f00e2
--- /dev/null
+++ b/style/style_test.go
@@ -0,0 +1,202 @@
+package style
+
+import (
+ "testing"
+
+ "golang.org/x/exp/shiny/unit"
+ "golang.org/x/image/font/gofont/gomono"
+ "golang.org/x/image/math/fixed"
+)
+
+const (
+ dpi = 150.0
+ epsilon = 0.5 // TODO: optimize precision
+)
+
+var (
+ fontTtf = gomono.TTF
+ units = []unit.Unit{unit.Px, unit.Dp, unit.Pt, unit.Mm, unit.In, unit.Em, unit.Ex, unit.Ch}
+)
+
+func TestConvert(t *testing.T) {
+ t.Parallel()
+
+ s, err := New(Font(fontTtf), Dpi(dpi))
+ if err != nil {
+ t.Error(err)
+ }
+ defer func() {
+ if err := s.Close(); err != nil {
+ t.Fatal(err)
+ }
+ }()
+
+ // Trivial
+ for _, from := range units {
+ for _, to := range units {
+ testConvert(t, s, unit.Value{0, from}, to, 0)
+ }
+ }
+
+ pxPerEm := fixed26ToFloat(s.face.Metrics().Height)
+ pxPerEx := fixed26ToFloat(s.face.Metrics().XHeight)
+ advance, ok := s.face.GlyphAdvance('0')
+ if !ok {
+ t.Fail()
+ }
+ pxPerCh := fixed26ToFloat(advance)
+
+ // px → all
+ px := 123.4
+ in := px / dpi
+ from := unit.Value{px, unit.Px}
+ testConvert(t, s, from, unit.Px, px)
+ testConvert(t, s, from, unit.Dp, in*160)
+ testConvert(t, s, from, unit.Pt, in*72)
+ testConvert(t, s, from, unit.Mm, in*25.4)
+ testConvert(t, s, from, unit.In, in)
+ testConvert(t, s, from, unit.Em, px/pxPerEm)
+ testConvert(t, s, from, unit.Ex, px/pxPerEx)
+ testConvert(t, s, from, unit.Ch, px/pxPerCh)
+
+ // dp → all
+ dp := 123.4
+ in = dp / 160
+ px = in * dpi
+ from = unit.Value{dp, unit.Dp}
+ testConvert(t, s, from, unit.Px, px)
+ testConvert(t, s, from, unit.Dp, dp)
+ testConvert(t, s, from, unit.Pt, in*72)
+ testConvert(t, s, from, unit.Mm, in*25.4)
+ testConvert(t, s, from, unit.In, in)
+ testConvert(t, s, from, unit.Em, px/pxPerEm)
+ testConvert(t, s, from, unit.Ex, px/pxPerEx)
+ testConvert(t, s, from, unit.Ch, px/pxPerCh)
+
+ // Pt → all
+ pt := 123.4
+ in = pt / 72
+ px = in * dpi
+ from = unit.Value{pt, unit.Pt}
+ testConvert(t, s, from, unit.Px, px)
+ testConvert(t, s, from, unit.Dp, in*160)
+ testConvert(t, s, from, unit.Pt, pt)
+ testConvert(t, s, from, unit.Mm, in*25.4)
+ testConvert(t, s, from, unit.In, in)
+ testConvert(t, s, from, unit.Em, px/pxPerEm)
+ testConvert(t, s, from, unit.Ex, px/pxPerEx)
+ testConvert(t, s, from, unit.Ch, px/pxPerCh)
+
+ // Mm → all
+ mm := 123.4
+ in = mm / 25.4
+ px = in * dpi
+ from = unit.Value{mm, unit.Mm}
+ testConvert(t, s, from, unit.Px, px)
+ testConvert(t, s, from, unit.Dp, in*160)
+ testConvert(t, s, from, unit.Pt, in*72)
+ testConvert(t, s, from, unit.Mm, mm)
+ testConvert(t, s, from, unit.In, in)
+ testConvert(t, s, from, unit.Em, px/pxPerEm)
+ testConvert(t, s, from, unit.Ex, px/pxPerEx)
+ testConvert(t, s, from, unit.Ch, px/pxPerCh)
+
+ // In → all
+ in = 123.4
+ px = in * dpi
+ from = unit.Value{in, unit.In}
+ testConvert(t, s, from, unit.Px, px)
+ testConvert(t, s, from, unit.Dp, in*160)
+ testConvert(t, s, from, unit.Pt, in*72)
+ testConvert(t, s, from, unit.Mm, in*25.4)
+ testConvert(t, s, from, unit.In, in)
+ testConvert(t, s, from, unit.Em, px/pxPerEm)
+ testConvert(t, s, from, unit.Ex, px/pxPerEx)
+ testConvert(t, s, from, unit.Ch, px/pxPerCh)
+
+ // Em → all
+ em := 123.4
+ px = em * pxPerEm
+ in = px / dpi
+ from = unit.Value{em, unit.Em}
+ testConvert(t, s, from, unit.Px, px)
+ testConvert(t, s, from, unit.Dp, in*160)
+ testConvert(t, s, from, unit.Pt, in*72)
+ testConvert(t, s, from, unit.Mm, in*25.4)
+ testConvert(t, s, from, unit.In, in)
+ testConvert(t, s, from, unit.Em, em)
+ testConvert(t, s, from, unit.Ex, px/pxPerEx)
+ testConvert(t, s, from, unit.Ch, px/pxPerCh)
+
+ // Ex → all
+ ex := 123.4
+ px = ex * pxPerEx
+ in = px / dpi
+ from = unit.Value{ex, unit.Ex}
+ testConvert(t, s, from, unit.Px, px)
+ testConvert(t, s, from, unit.Dp, in*160)
+ testConvert(t, s, from, unit.Pt, in*72)
+ testConvert(t, s, from, unit.Mm, in*25.4)
+ testConvert(t, s, from, unit.In, in)
+ testConvert(t, s, from, unit.Em, px/pxPerEm)
+ testConvert(t, s, from, unit.Ex, ex)
+ testConvert(t, s, from, unit.Ch, px/pxPerCh)
+
+ // Ch → all
+ ch := 123.4
+ px = ch * pxPerCh
+ in = px / dpi
+ from = unit.Value{ch, unit.Ch}
+ testConvert(t, s, from, unit.Px, px)
+ testConvert(t, s, from, unit.Dp, in*160)
+ testConvert(t, s, from, unit.Pt, in*72)
+ testConvert(t, s, from, unit.Mm, in*25.4)
+ testConvert(t, s, from, unit.In, in)
+ testConvert(t, s, from, unit.Em, px/pxPerEm)
+ testConvert(t, s, from, unit.Ex, px/pxPerEx)
+ testConvert(t, s, from, unit.Ch, ch)
+}
+
+func testConvert(t *testing.T, s *Style, from unit.Value, to unit.Unit, want float64) {
+ out := s.Convert(from, to)
+ if out.U != to || out.F < want-epsilon || out.F > want+epsilon {
+ t.Errorf("Convert(%v, %v) = %v; want %v", from, to, out, unit.Value{want, to})
+ }
+}
+
+func TestPixels(t *testing.T) {
+ t.Parallel()
+
+ s, err := New(Font(fontTtf), Dpi(dpi))
+ if err != nil {
+ t.Error(err)
+ }
+ defer func() {
+ if err := s.Close(); err != nil {
+ t.Fatal(err)
+ }
+ }()
+
+ for _, u := range units {
+ testPixels(t, s, unit.Value{0, u}, 0)
+ }
+ testPixels(t, s, unit.Value{123.4, unit.Px}, floatToFixed26(123.4))
+ testPixels(t, s, unit.Value{123.4, unit.Dp}, floatToFixed26(123.4/160*dpi))
+ testPixels(t, s, unit.Value{123.4, unit.Pt}, floatToFixed26(123.4/72*dpi))
+ testPixels(t, s, unit.Value{123.4, unit.Mm}, floatToFixed26(123.4/25.4*dpi))
+ testPixels(t, s, unit.Value{123.4, unit.In}, floatToFixed26(123.4*dpi))
+ testPixels(t, s, unit.Value{123.4, unit.Em}, floatToFixed26(123.4).Mul(s.face.Metrics().Height))
+ testPixels(t, s, unit.Value{123.4, unit.Ex}, floatToFixed26(123.4).Mul(s.face.Metrics().XHeight))
+ if advance, ok := s.face.GlyphAdvance('0'); ok {
+ testPixels(t, s, unit.Value{123.4, unit.Ch}, floatToFixed26(123.4).Mul(advance))
+ } else {
+ t.Fail()
+ }
+}
+
+func testPixels(t *testing.T, s *Style, in unit.Value, want fixed.Int26_6) {
+ out := s.Pixels(in)
+ if out != want {
+ t.Errorf("Pixels(%#v) = %v; want %v", in, out, want)
+ }
+}