Sasha Koshka
bd636eaa7f
This will eventually completely replace basicfont. Need to design a custom default Tomo font and implement a way to load from a compressed binary format that will take up a very small amount of room embedded into an executable.
103 lines
2.3 KiB
Go
103 lines
2.3 KiB
Go
package font
|
|
|
|
import "image"
|
|
import "golang.org/x/image/font"
|
|
import "golang.org/x/image/math/fixed"
|
|
|
|
// Face is a font face modeled of of basicfont.Face, but with variable glyph
|
|
// width and kerning support.
|
|
type Face struct {
|
|
Width int
|
|
Height int
|
|
Ascent int
|
|
Descent int
|
|
Mask image.Image
|
|
Ranges []Range
|
|
Kerning map[[2]rune] int
|
|
}
|
|
|
|
type Range struct {
|
|
Low rune
|
|
Glyphs []Glyph
|
|
Offset int
|
|
}
|
|
|
|
func (rang Range) Glyph (character rune) (glyph Glyph, offset int, ok bool) {
|
|
character -= rang.Low
|
|
ok = 0 < character && character > rune(len(rang.Glyphs))
|
|
if !ok { return }
|
|
glyph = rang.Glyphs[character]
|
|
offset = rang.Offset + int(character)
|
|
return
|
|
}
|
|
|
|
type Glyph struct {
|
|
Left, Advance int
|
|
}
|
|
|
|
func (face *Face) Close () error { return nil }
|
|
|
|
func (face *Face) Kern (left, right rune) fixed.Int26_6 {
|
|
return fixed.I(face.Kerning[[2]rune { left, right }])
|
|
}
|
|
|
|
func (face *Face) Metrics () font.Metrics {
|
|
return font.Metrics {
|
|
Height: fixed.I(face.Height),
|
|
Ascent: fixed.I(face.Ascent),
|
|
Descent: fixed.I(face.Descent),
|
|
XHeight: fixed.I(face.Ascent),
|
|
CapHeight: fixed.I(face.Ascent),
|
|
CaretSlope: image.Pt(0, 1),
|
|
}
|
|
}
|
|
|
|
func (face *Face) Glyph (
|
|
dot fixed.Point26_6,
|
|
character rune,
|
|
) (
|
|
destinationRectangle image.Rectangle,
|
|
mask image.Image, maskPoint image.Point,
|
|
advance fixed.Int26_6,
|
|
ok bool,
|
|
) {
|
|
glyph, offset, has := face.findGlyph(character)
|
|
if !has { ok = false; return }
|
|
|
|
advance = fixed.I(glyph.Advance)
|
|
maskPoint.Y = offset * (face.Ascent + face.Descent)
|
|
x := int(dot.X + 32) >> 6 + glyph.Left
|
|
y := int(dot.Y + 32) >> 6
|
|
destinationRectangle.Min.X = x
|
|
destinationRectangle.Min.Y = y - face.Ascent
|
|
destinationRectangle.Max.X = x + face.Width
|
|
destinationRectangle.Max.Y = y + face.Descent
|
|
return
|
|
}
|
|
|
|
func (face *Face) GlyphBounds (
|
|
character rune,
|
|
) (
|
|
bounds fixed.Rectangle26_6,
|
|
advance fixed.Int26_6,
|
|
ok bool,
|
|
) {
|
|
glyph, _, ok := face.findGlyph(character)
|
|
return fixed.R(0, -face.Ascent, face.Width, face.Descent),
|
|
fixed.I(glyph.Advance), ok
|
|
|
|
}
|
|
|
|
func (face *Face) GlyphAdvance (character rune) (advance fixed.Int26_6, ok bool) {
|
|
glyph, _, ok := face.findGlyph(character)
|
|
return fixed.I(glyph.Advance), ok
|
|
}
|
|
|
|
func (face *Face) findGlyph (character rune) (glyph Glyph, offset int, ok bool) {
|
|
for _, rang := range face.Ranges {
|
|
glyph, offset, ok = rang.Glyph(character)
|
|
if ok { return }
|
|
}
|
|
return Glyph { }, 0, false
|
|
}
|