From 2ccd5358dc98c131189fc17285d439ae91bc4041 Mon Sep 17 00:00:00 2001 From: Ben Perry Date: Wed, 11 Sep 2024 01:30:01 -0500 Subject: [PATCH 1/6] Apply offset from orig to bounds.Min before anchoring to correctly align text bounds to anchor --- ext/text/text.go | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/ext/text/text.go b/ext/text/text.go index f4c4f6d..a54651b 100644 --- a/ext/text/text.go +++ b/ext/text/text.go @@ -195,6 +195,17 @@ func (txt *Text) AlignedTo(anchor pixel.Anchor) *Text { return txt } +func (txt *Text) AnchoredOffset() pixel.Vec { + offset := txt.bounds.AnchorPos(txt.anchor) + height := txt.bounds.H() + if height > 0 { + // Origin marks bottom of first line, while bounds wrap all lines of text + // To correctly align anchoring, offset by the height minus the first line's height + offset.Y += height - txt.atlas.lineHeight + } + return offset +} + // Clear removes all written text from the Text. The Dot field is reset to Orig. func (txt *Text) Clear() { txt.prevR = -1 @@ -253,14 +264,14 @@ func (txt *Text) Draw(t pixel.Target, matrix pixel.Matrix) { // If there's a lot of text written to the Text, changing a matrix or a color mask often might hurt // performance. Consider using your Target's SetMatrix or SetColorMask methods if available. func (txt *Text) DrawColorMask(t pixel.Target, matrix pixel.Matrix, mask color.Color) { + offset := txt.AnchoredOffset() + matrix = pixel.IM.Moved(offset).Chained(matrix) + if matrix != txt.mat { txt.mat = matrix txt.dirty = true } - offset := txt.Bounds().AnchorPos(txt.anchor) - txt.mat = pixel.IM.Moved(offset).Chained(txt.mat) - if mask == nil { mask = pixel.Alpha(1) } From d8e969d641ff398320cdb992571a10c65ec7b391 Mon Sep 17 00:00:00 2001 From: Ben Perry Date: Wed, 11 Sep 2024 01:31:12 -0500 Subject: [PATCH 2/6] Draw text without anchoring by default to preserve existing behavior --- ext/text/text.go | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/ext/text/text.go b/ext/text/text.go index a54651b..f6ef645 100644 --- a/ext/text/text.go +++ b/ext/text/text.go @@ -100,12 +100,13 @@ type Text struct { glyph pixel.TrianglesData tris pixel.TrianglesData - mat pixel.Matrix - col pixel.RGBA - trans pixel.TrianglesData - transD pixel.Drawer - dirty bool - anchor pixel.Anchor + mat pixel.Matrix + col pixel.RGBA + trans pixel.TrianglesData + transD pixel.Drawer + dirty bool + anchor pixel.Anchor + isAnchored bool } // New creates a new Text capable of drawing runes contained in the provided Atlas. Orig and Dot @@ -192,10 +193,23 @@ func (txt *Text) BoundsOf(s string) pixel.Rect { // AlignedTo returns the text moved by the given anchor. func (txt *Text) AlignedTo(anchor pixel.Anchor) *Text { txt.anchor = anchor + txt.isAnchored = true + return txt +} + +// Unaligned removes anchoring from the text +func (txt *Text) Unaligned() *Text { + var anchor pixel.Anchor + txt.anchor = anchor + txt.isAnchored = false return txt } func (txt *Text) AnchoredOffset() pixel.Vec { + if !txt.isAnchored { + return pixel.ZV + } + offset := txt.bounds.AnchorPos(txt.anchor) height := txt.bounds.H() if height > 0 { @@ -264,8 +278,10 @@ func (txt *Text) Draw(t pixel.Target, matrix pixel.Matrix) { // If there's a lot of text written to the Text, changing a matrix or a color mask often might hurt // performance. Consider using your Target's SetMatrix or SetColorMask methods if available. func (txt *Text) DrawColorMask(t pixel.Target, matrix pixel.Matrix, mask color.Color) { - offset := txt.AnchoredOffset() - matrix = pixel.IM.Moved(offset).Chained(matrix) + if txt.isAnchored { + offset := txt.AnchoredOffset() + matrix = pixel.IM.Moved(offset).Chained(matrix) + } if matrix != txt.mat { txt.mat = matrix From 64b3d7e7645878cf0ae3a128ed1af56067ff6f5b Mon Sep 17 00:00:00 2001 From: Ben Perry Date: Wed, 11 Sep 2024 01:31:32 -0500 Subject: [PATCH 3/6] Helper functions to find the anchored position of Dot and Bounds --- ext/text/text.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/ext/text/text.go b/ext/text/text.go index f6ef645..bcd0a24 100644 --- a/ext/text/text.go +++ b/ext/text/text.go @@ -205,6 +205,20 @@ func (txt *Text) Unaligned() *Text { return txt } +func (txt *Text) AnchoredBounds() pixel.Rect { + if !txt.isAnchored { + return txt.bounds + } + return txt.bounds.Moved(txt.AnchoredOffset()) +} + +func (txt *Text) AnchoredDot() pixel.Vec { + if !txt.isAnchored { + return txt.Dot + } + return txt.AnchoredOffset().Add(txt.Dot) +} + func (txt *Text) AnchoredOffset() pixel.Vec { if !txt.isAnchored { return pixel.ZV From b79fe6de2323d74185dec7a8358af788a5a7ada8 Mon Sep 17 00:00:00 2001 From: Ben Perry Date: Wed, 11 Sep 2024 01:31:49 -0500 Subject: [PATCH 4/6] Expand bounds based on control runes --- ext/text/text.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/ext/text/text.go b/ext/text/text.go index bcd0a24..c82a65b 100644 --- a/ext/text/text.go +++ b/ext/text/text.go @@ -334,6 +334,12 @@ func (txt *Text) controlRune(r rune, dot pixel.Vec) (newDot pixel.Vec, control b case '\n': dot.X = txt.Orig.X dot.Y -= txt.LineHeight + if txt.bounds.Empty() { + txt.bounds.Min = dot + txt.bounds.Max = txt.Orig.Add(pixel.V(0.01, txt.atlas.lineHeight)) + } else { + txt.bounds.Min.Y -= txt.LineHeight + } case '\r': dot.X = txt.Orig.X case '\t': @@ -343,6 +349,12 @@ func (txt *Text) controlRune(r rune, dot pixel.Vec) (newDot pixel.Vec, control b rem = txt.TabWidth } dot.X += rem + if txt.bounds.Empty() { + txt.bounds.Min = txt.Dot + txt.bounds.Max = pixel.V(dot.X, txt.Orig.Y+txt.atlas.lineHeight) + } else if dot.X > txt.bounds.Max.X { + txt.bounds.Max.X = dot.X + } default: return dot, false } From e39b84180f98c54e6f94f62860f98cca36a21b28 Mon Sep 17 00:00:00 2001 From: Ben Perry Date: Wed, 11 Sep 2024 01:32:04 -0500 Subject: [PATCH 5/6] Expand bounds for space characters if font does not properly compute bounds --- ext/text/text.go | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/ext/text/text.go b/ext/text/text.go index c82a65b..626fed4 100644 --- a/ext/text/text.go +++ b/ext/text/text.go @@ -381,8 +381,20 @@ func (txt *Text) drawBuf() { continue } + var dot pixel.Vec var rect, frame, bounds pixel.Rect - rect, frame, bounds, txt.Dot = txt.Atlas().DrawRune(txt.prevR, r, txt.Dot) + rect, frame, bounds, dot = txt.Atlas().DrawRune(txt.prevR, r, txt.Dot) + if r == ' ' { + // Space character has empty bounds for some fonts + if bounds.W() == 0 { + bounds.Max = bounds.Max.Add(dot.Sub(txt.Dot)) + } + if bounds.H() == 0 { + bounds.Min = txt.Dot + bounds.Max.Y += txt.atlas.lineHeight + } + } + txt.Dot = dot txt.prevR = r From b1343a51cb15dcd3c4db3a25d3ddd4be52517ffa Mon Sep 17 00:00:00 2001 From: Ben Perry Date: Wed, 11 Sep 2024 02:09:00 -0500 Subject: [PATCH 6/6] Comments --- ext/text/text.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ext/text/text.go b/ext/text/text.go index 626fed4..20f9d8c 100644 --- a/ext/text/text.go +++ b/ext/text/text.go @@ -205,6 +205,7 @@ func (txt *Text) Unaligned() *Text { return txt } +// AnchoredBounds returns the text bounds with the anchoring offset applied func (txt *Text) AnchoredBounds() pixel.Rect { if !txt.isAnchored { return txt.bounds @@ -212,6 +213,7 @@ func (txt *Text) AnchoredBounds() pixel.Rect { return txt.bounds.Moved(txt.AnchoredOffset()) } +// AnchoredDot returns text.Dot with the anchoring offset applied func (txt *Text) AnchoredDot() pixel.Vec { if !txt.isAnchored { return txt.Dot @@ -219,6 +221,9 @@ func (txt *Text) AnchoredDot() pixel.Vec { return txt.AnchoredOffset().Add(txt.Dot) } +// AnchoredOffset calculates the position offset for the text based on it's anchor +// +// Text is anchored relative to the Orig func (txt *Text) AnchoredOffset() pixel.Vec { if !txt.isAnchored { return pixel.ZV