204 lines
4.9 KiB
Go
204 lines
4.9 KiB
Go
package gel
|
|
|
|
import (
|
|
"image"
|
|
"testing"
|
|
|
|
"gioui.org/text"
|
|
"golang.org/x/image/math/fixed"
|
|
)
|
|
|
|
func TestFixedToFloat(t *testing.T) {
|
|
tests := []struct {
|
|
input fixed.Int26_6
|
|
want float32
|
|
}{
|
|
{fixed.I(0), 0.0},
|
|
{fixed.I(1), 1.0},
|
|
{fixed.I(10), 10.0},
|
|
{fixed.I(64), 64.0},
|
|
{fixed.Int26_6(32), 0.5}, // 32/64 = 0.5
|
|
{fixed.Int26_6(96), 1.5}, // 96/64 = 1.5
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
got := FixedToFloat(tc.input)
|
|
if got != tc.want {
|
|
t.Errorf("FixedToFloat(%v) = %v, want %v", tc.input, got, tc.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestGlyphIteratorProcessGlyph(t *testing.T) {
|
|
it := &GlyphIterator{
|
|
Viewport: image.Rectangle{Max: image.Pt(100, 100)},
|
|
MaxLines: 0,
|
|
}
|
|
|
|
// Create a test glyph within the viewport
|
|
g := text.Glyph{
|
|
X: fixed.I(10),
|
|
Y: 20,
|
|
Advance: fixed.I(10),
|
|
Ascent: fixed.I(12),
|
|
Descent: fixed.I(4),
|
|
Bounds: fixed.Rectangle26_6{
|
|
Min: fixed.Point26_6{X: fixed.I(0), Y: fixed.I(-12)},
|
|
Max: fixed.Point26_6{X: fixed.I(10), Y: fixed.I(4)},
|
|
},
|
|
}
|
|
|
|
visibleOrBefore := it.ProcessGlyph(g, true)
|
|
if !visibleOrBefore {
|
|
t.Error("ProcessGlyph returned false for visible glyph")
|
|
}
|
|
if !it.Visible {
|
|
t.Error("glyph should be marked visible")
|
|
}
|
|
if !it.First {
|
|
t.Error("First should be true after first glyph")
|
|
}
|
|
}
|
|
|
|
func TestGlyphIteratorProcessGlyphOutsideViewport(t *testing.T) {
|
|
it := &GlyphIterator{
|
|
Viewport: image.Rectangle{Max: image.Pt(100, 100)},
|
|
MaxLines: 0,
|
|
}
|
|
|
|
// Create a glyph below the viewport
|
|
g := text.Glyph{
|
|
X: fixed.I(10),
|
|
Y: 200, // Below viewport
|
|
Advance: fixed.I(10),
|
|
Ascent: fixed.I(12),
|
|
Descent: fixed.I(4),
|
|
Bounds: fixed.Rectangle26_6{
|
|
Min: fixed.Point26_6{X: fixed.I(0), Y: fixed.I(-12)},
|
|
Max: fixed.Point26_6{X: fixed.I(10), Y: fixed.I(4)},
|
|
},
|
|
}
|
|
|
|
visibleOrBefore := it.ProcessGlyph(g, true)
|
|
// Should return false because glyph is below viewport
|
|
if visibleOrBefore {
|
|
t.Error("ProcessGlyph should return false for glyph below viewport")
|
|
}
|
|
if it.Visible {
|
|
t.Error("glyph below viewport should not be marked visible")
|
|
}
|
|
}
|
|
|
|
func TestGlyphIteratorMaxLines(t *testing.T) {
|
|
it := &GlyphIterator{
|
|
Viewport: image.Rectangle{Max: image.Pt(100, 100)},
|
|
MaxLines: 2,
|
|
}
|
|
|
|
// Simulate two line breaks
|
|
g := text.Glyph{
|
|
X: fixed.I(10),
|
|
Y: 10,
|
|
Advance: fixed.I(10),
|
|
Ascent: fixed.I(12),
|
|
Descent: fixed.I(4),
|
|
Flags: text.FlagLineBreak,
|
|
Bounds: fixed.Rectangle26_6{
|
|
Min: fixed.Point26_6{X: fixed.I(0), Y: fixed.I(-12)},
|
|
Max: fixed.Point26_6{X: fixed.I(10), Y: fixed.I(4)},
|
|
},
|
|
}
|
|
|
|
// First line break
|
|
it.ProcessGlyph(g, true)
|
|
if it.LinesSeen != 1 {
|
|
t.Errorf("LinesSeen = %d after first line break, want 1", it.LinesSeen)
|
|
}
|
|
|
|
// Second line break
|
|
it.ProcessGlyph(g, true)
|
|
if it.LinesSeen != 2 {
|
|
t.Errorf("LinesSeen = %d after second line break, want 2", it.LinesSeen)
|
|
}
|
|
}
|
|
|
|
func TestGlyphIteratorBoundsAccumulation(t *testing.T) {
|
|
it := &GlyphIterator{
|
|
Viewport: image.Rectangle{Max: image.Pt(200, 200)},
|
|
MaxLines: 0,
|
|
}
|
|
|
|
// Process multiple glyphs and check bounds accumulate
|
|
glyphs := []text.Glyph{
|
|
{X: fixed.I(0), Y: 10, Advance: fixed.I(10), Ascent: fixed.I(8), Descent: fixed.I(2)},
|
|
{X: fixed.I(10), Y: 10, Advance: fixed.I(10), Ascent: fixed.I(8), Descent: fixed.I(2)},
|
|
{X: fixed.I(20), Y: 10, Advance: fixed.I(10), Ascent: fixed.I(8), Descent: fixed.I(2)},
|
|
}
|
|
|
|
for i, g := range glyphs {
|
|
g.Bounds = fixed.Rectangle26_6{
|
|
Min: fixed.Point26_6{X: fixed.I(0), Y: fixed.I(-8)},
|
|
Max: fixed.Point26_6{X: fixed.I(10), Y: fixed.I(2)},
|
|
}
|
|
it.ProcessGlyph(g, true)
|
|
if i == 0 && !it.First {
|
|
t.Error("First should be true after processing first glyph")
|
|
}
|
|
}
|
|
|
|
// Bounds should encompass all glyphs
|
|
if it.Bounds.Max.X < 30 {
|
|
t.Errorf("Bounds.Max.X = %d, should be >= 30", it.Bounds.Max.X)
|
|
}
|
|
}
|
|
|
|
func TestGlyphIteratorPaddingCalculation(t *testing.T) {
|
|
it := &GlyphIterator{
|
|
Viewport: image.Rectangle{Max: image.Pt(100, 100)},
|
|
MaxLines: 0,
|
|
}
|
|
|
|
g := text.Glyph{
|
|
X: fixed.I(10),
|
|
Y: 20,
|
|
Advance: fixed.I(10),
|
|
Ascent: fixed.I(12),
|
|
Descent: fixed.I(4),
|
|
Bounds: fixed.Rectangle26_6{
|
|
Min: fixed.Point26_6{X: fixed.I(-2), Y: fixed.I(-14)}, // Extends left and above
|
|
Max: fixed.Point26_6{X: fixed.I(12), Y: fixed.I(6)}, // Extends right and below
|
|
},
|
|
}
|
|
|
|
it.ProcessGlyph(g, true)
|
|
|
|
// Padding should capture the glyph bounds that extend beyond advance/metrics
|
|
if it.Padding.Min.X >= 0 {
|
|
t.Errorf("Padding.Min.X = %d, expected negative for left extension", it.Padding.Min.X)
|
|
}
|
|
}
|
|
|
|
func BenchmarkGlyphIteratorProcessGlyph(b *testing.B) {
|
|
it := &GlyphIterator{
|
|
Viewport: image.Rectangle{Max: image.Pt(1000, 1000)},
|
|
MaxLines: 0,
|
|
}
|
|
|
|
g := text.Glyph{
|
|
X: fixed.I(10),
|
|
Y: 20,
|
|
Advance: fixed.I(10),
|
|
Ascent: fixed.I(12),
|
|
Descent: fixed.I(4),
|
|
Bounds: fixed.Rectangle26_6{
|
|
Min: fixed.Point26_6{X: fixed.I(0), Y: fixed.I(-12)},
|
|
Max: fixed.Point26_6{X: fixed.I(10), Y: fixed.I(4)},
|
|
},
|
|
}
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
it.ProcessGlyph(g, true)
|
|
}
|
|
}
|